Zum Hauptinhalt springen

Reaktionsfähige und anpassungsfähige Entwicklung

Auf der Suche nach Designrichtlinien? Siehe Reaktionsfähiges und anpassungsfähiges Design.

Bedingungen​

Jutro-Komponenten können sein:

  • Reaktionsfähig: Die Komponente passt sich mithilfe von Medienabfragen automatisch an die Bildschirmgröße an.
  • Anpassungsfähig: Die Komponente passt sich nicht an die Bildschirmgröße an. Stattdessen müssen Sie sie mithilfe der Breakpoint-Eigenschaften konfigurieren. Die Komponente wird je nach Gerät unterschiedlich gerendert.
  • Fixiert: Die Komponente passt sich nicht an die Bildschirmgröße an.

In Storybook ist jede Komponente in der oberen Leiste als reaktionsfähig, anpassungsfähig oder fixiert gekennzeichnet.

Die meisten Komponenten sind zwar auf allen Gerätetypen reaktionsfähig, aber einige lassen sich auf kleinen Geräten möglicherweise nicht so gut verwenden wie auf großen. Wir empfehlen Ihnen, diese Komponenten mit Breakpoint-Eigenschaften zu konfigurieren.

Zusammenarbeit mit Designern​

Ein Designer kann Ihnen App-Entwürfe für verschiedene Breakpoints zur Verfügung stellen. Fragen Sie ihn nach den erwarteten Verhaltensweisen usw., um sicherzustellen, dass Sie verstehen, wie der Benutzer das Beste aus der Anwendung herausholen kann.

Im Folgenden finden Sie allgemeine Tipps für die Zusammenarbeit mit Designern:

  • Achten Sie auf Layouts derselben Benutzeroberfläche für verschiedene Bildschirmgrößen.
  • Designer verwenden ggf. den Begriff „Formfaktor“, um sich auf einen bestimmten Breakpoint zu beziehen: phone, phoneWide, tablet oder desktop. Die genauen Breiten finden Sie unter Breakpoint-Eigenschaften .
  • Verwenden Sie die „Inspektions“-Tools in der Design-App (z. B. Figma), um die gewünschten Abstände und Größen zu ermitteln. Stellen Sie sicher, dass Sie Abstands- und Größenvariablen von Jutro verwenden (siehe Theming).
  • Zeigen Sie Ihre App so bald wie möglich einem Designer, um Feedback zu erhalten und schrittweise weitere Funktionen zu implementieren.
  • Holen Sie sich Klarheit über Dinge, die Sie nicht verstehen oder bei denen Sie sich nicht sicher sind, wie sie funktionieren sollen.
  • Verwenden Sie die Entwicklertools in Ihrem Browser, um das Layout an verschiedenen Breakpoints zu testen.
  • Wenn die Anwendung auf Telefonen funktionieren soll, entwickeln Sie sie zuerst für Telefone und gehen Sie später zu größeren Formaten über.

Machbarkeit​

Besprechen Sie die Entwürfe mit dem Designer und arbeiten Sie an der Machbarkeit. Dieser Prozess wird auch als „UI QA“ (Qualitätssicherung) bezeichnet:

  • Sind alle Komponenten verfügbar? Sie werden beispielsweise aufgefordert, ein Bildkarussell bereitzustellen, das jedoch keine Jutro-Standardkomponente ist. Das bedeutet, dass Sie Ihr eigenes erstellen müssen.
  • Funktionieren alle Komponenten wie vorgesehen? Möglicherweise müssen Sie an dieser Stelle eine App für den Machbarkeitsnachweis erstellen.
  • Wenn komplexe Funktionen oder Interaktionen auftreten, prüfen Sie, ob diese in Jutro erlaubt und kostengünstig zu implementieren sind. Ist zum Beispiel das Karussell der beste Weg, um den Anwendungsfall abzubilden? Es wird eine Option benötigt, damit an Kinetose leidende Benutzer den Bildwechsel stoppen können. Ist es machbar, diese Funktionalität im Rahmen Ihres Projekts zu erstellen?

Breakpoint-Eigenschaften​

Die Breakpoint-Eigenschaften sind:

Name der EigenschaftBreite
phone< 580px
phoneWide (phone in horizontal mode)580px to 768px
tablet768px to 1024px
desktop> 1024px

Verwenden Sie diese Eigenschaften, um das Verhalten Ihrer Komponente auf verschiedenen Geräten zu überschreiben.

Override children​

For example, you want your action title bar to not include an action when the user is on a mobile device. In that case, you can use the phone prop to override the children of the component.

<ActionTitleBar
phone={{
children: (
<TitleElement>
{/* On a phone, render only a heading */}
<h4>Just a title</h4>
</TitleElement>
),
}}>
{/* On large screens, render an icon with a tooltip and a button */}
<TitleElement>
<h4>Title</h4>
<TooltipIcon {...tooltipIconProps} />
<Button {...buttonProps}>Edit</Button>
</TitleElement>
</ActionTitleBar>

Mit Breakpoint-Eigenschaften können Sie die meisten Komponenten auf verschiedenen Geräten unterschiedlich rendern.

Eigenschaften überschreiben​

Diese Eigenschaften überschreiben die Komponenten-Eigenschaften beim Rendern auf einem bestimmten Gerät. Beispiel:

<RadioButtonField
availableValues={[
{
displayName: 'Perform payment on checkout',
},
{
displayName: 'Defer payment to a later date',
},
]}
phone={{
availableValues: [
{
displayName: 'Pay now',
},
{
displayName: 'Pay later',
},
],
}}
/>

Dies ist ein sehr einfaches Beispiel für die Anzeige unterschiedlicher Beschriftungen für Kontrollkästchen in kleinen Fenstern. Normalerweise würden Sie Intl-Meldungen verwenden, um Text für eine Komponente zu rendern. Aber dieses Beispiel veranschaulicht, wie Sie eine Kopie der Eigenschaft availableValues übergeben und die darin enthaltenen Elemente überschreiben.

Stile überschreiben​

Ein weiteres Beispiel ist das Überschreiben von Stilen für verschiedene Geräte:

<Card
className={styles.largeBanner}
phone={{
className: styles.smallBanner,
}}
tablet={{
className: styles.mediumBanner,
}}
/>

In diesem Beispiel haben wir eine Karte, die je nach Größe des Geräts unterschiedliche Stile aufweist.

Der useBreakpoint-Hook​

Note: Für neue Komponenten, die in Jutro 10 eingeführt wurden, sowie als allgemeine Empfehlung für diesen Zweck, empfehlen wir die Verwendung des useBreakpoint-Hooks. Breakpoint-Eigenschaften wie phone, tablet und phoneWide sind bei neuen Komponenten nicht standardmäßig verfügbar.

Der useBreakpoint-Hook wird aus @jutro/layout exportiert und kann wie import { useBreakpoint } from '@jutro/layout' importiert werden. Er gibt ein Objekt mit den folgenden Eigenschaften zurück:

  • breakpointProps – die Eigenschaften, die Sie der Komponente übergeben haben, zusammengeführt mit den Werten, die Sie in einer der folgenden Eigenschaften überschrieben haben: phone, phoneWide, tablet
  • applyBreakpoint – eine Funktion, die props als Argument entgegennimmt breakpointProps und zurückgibt. Sie können sie in jedem verschachtelten Code innerhalb Ihrer Komponente verwenden, in dem Hooks nicht erlaubt sind.
  • breakpoint – der Name des aktuellen Breakpoints. Möglich sind phone, phoneWide, tablet oder desktop.

Es gibt vier Möglichkeiten, wie Sie den Hook einsetzen können, um das erwartete Ergebnis zu erzielen.

1. Abrufen von Eigenschaften aus dem Hook für einen bestimmten Breakpoint​

Die erste Möglichkeit, den Hook zu verwenden, besteht darin, die Eigenschaften zu übergeben und die Ausgabe auf die Komponente anzuwenden.

Im folgenden Beispiel wird das phone-Eigenschaftenobjekt an useBreakpoint übergeben. Die Eigenschaften werden dann als textInputPhoneProps am Breakpoint phone zurückgegeben. Dann werden sie TextInput zugewiesen und die zuvor hinzugefügten Eigenschaften werden überschrieben. Die label-Eigenschaft wird überschrieben, dann werden placeholder und className hinzugefügt, wenn der Hook erkennt, dass sich der Breakpoint in phone ändert.

const { breakpointProps: textInputPhoneProps } = useBreakpoint({
phone: {
placeholder: 'Phone placeholder',
label: 'Phone label',
className: 'somePhoneClass',
},
});
return (
<TextInput
label="Text input"
{...textInputPhoneProps}
/>
);

2. Nur Breakpoint-Status vom Hook abrufen​

Die zweite Möglichkeit zur Verwendung des Hooks besteht darin, nur den Status des Breakpoints abzurufen und diese Eigenschaft auf der Grundlage des zurückgegebenen Breakpoints zu ändern. Im folgenden Beispiel wird breakpoint vom Hook abgerufen und ändert sich je nach Viewport. Ein anderer Wert kann für die label-Eigenschaft zugewiesen werden, wenn der Breakpoint auf phone gesetzt wird.

const { breakpoint } = useBreakpoint({});

return (
<TextInput label={breakpoint === 'phone' ? 'Phone label' : 'Text input'} />
);

3. Abrufen des Callback, der die Breakpoint-Eigenschaften zurückgibt​

Die dritte Möglichkeit, den Hook zu verwenden, ist die Verwendung von applyBreakpoint. – Sie ist der ersten Option sehr ähnlich, kann aber besser geeignet sein, wenn es verschiedene Komponenten auf derselben Seite gibt, die viele useBreakPoints-Instanzen erfordern würden. Im folgenden Beispiel wird der Callback, der die Eigenschaften zurückgibt, abgerufen und dann wird applyBreakpoint jedem TextInput zugewiesen, wobei jeder Komponente unterschiedliche Eigenschaften übergeben werden.

const { applyBreakpoint } = useBreakpoint({});

return (
<React.Fragment>
<TextInput
label="Text input"
{...applyBreakpoint({
phone: {
placeholder: 'Phone placeholder',
label: 'Phone label',
className: 'somePhoneClass',
},
})}
/>
<TextInput
label="Text input instance 2"
{...applyBreakpoint({
phone: {
placeholder: 'Another Phone placeholder',
label: 'Another Phone label',
className: 'someOtherPhoneClass',
},
})}
/>
</React.Fragment>
);

4. Alle auf einmal​

Es ist möglich, alle drei Optionen, die von useBreakpoint zurückgegeben werden, abzurufen und alle auf einmal zu verwenden. Beispiel:

const {
breakpointProps: textInputPhoneProps,
breakpoint,
applyBreakpoint,
} = useBreakpoint({
phone: {
placeholder: 'Phone placeholder',
label: 'Phone label',
className: 'somePhoneClass',
},
});

return (
<React.Fragment>
<TextInput
label="Text input"
{...textInputPhoneProps}
/>
<TextInput
label={breakpoint === 'phone' ? 'Phone label 2' : 'Text input'}
/>
<TextInput
label="Text input"
{...applyBreakpoint({
phone: {
placeholder: 'Phone placeholder 3',
label: 'Phone label 3',
className: 'someOtherPhoneClass',
},
})}
/>
</React.Fragment>
);

Verschiedene Eigenschaften für einen Breakpoint​

Ein typischer Anwendungsfall für den Hook ist, wenn Sie einer Komponente auf der Grundlage des Breakpoints benutzerdefinierte Eigenschaften übergeben möchten. Wenn Sie die Komponente verwenden, gehen Sie wie folgt vor:

<BigBanner
phone={{
message: 'Call us!',
}}
message="Get in touch"
/>

Das bedeutet, dass Sie möchten, dass Ihre Benutzer in den meisten Fällen die Meldung „Kontaktieren Sie uns“ erhalten. Aber wenn sie am Telefon sind, möchten Sie, dass sie die Meldung „Rufen Sie uns an!“ erhalten.

Innerhalb der funktionalen Komponente verwenden Sie den useBreakpoint-Hook und übergeben die Eigenschaften als einziges Argument. Dann extrahieren Sie den Wert der message-Eigenschaft.

Auf dem Desktop gibt er „Kontaktieren Sie uns“ zurück und auf dem Telefon „Rufen Sie uns an!“.

import { useBreakpoint } from '@jutro/layout';

function BigBanner(props) {
const { breakpointProps } = useBreakpoint(props);

return <div>{breakpointProps.message}</div>;
}

Verschiedene Stile für einen Breakpoint​

Ein weiterer Anwendungsfall für den Hook ist die Anwendung eines anderen Stils auf eine Komponente, basierend auf dem aktuellen Breakpoint.

Sie erstellen zwei Klassen in Ihrem Stile-Modul:

BigBanner.module.scss
.defaultColor {
color: magenta;
}

.phoneColor {
color: blue;
}

Um die Stile zu verwenden, ermitteln Sie den aktuellen Breakpoint mithilfe des Hooks. Anschließend wählen Sie eine Klasse auf der Grundlage des Hook-Wertes aus.

BigBanner.js
import React from 'react';
import { useBreakpoint } from '@jutro/layout';
import styles from './BigBanner.module.scss';

function BigBanner(props) {
const { breakpoint } = useBreakpoint(props);

const myClass =
breakpoint === 'phone' ? styles.phoneColor : styles.defaultColor;

return <div className={myClass}>Welcome to my banner!</div>;
}

Oder:

BigBanner.js
import React from 'react';
import cx from 'classnames';
import styles from './BigBanner.module.scss';
import { useBreakpoint } from '@jutro/layout';

function BigBanner(props) {
const { breakpointProps } = useBreakpoint({
...props,
className: cx(props.className, styles.defaultColor),
phone: {
className: cx(props.className, styles.phoneColor),
},
});
const { className } = breakpointProps;

return <div className={className}>{breakpointProps.message}</div>;
}

Der BreakpointTracker-Kontext​

Sie können BreakpointTrackerContext und BreakpointTracker auch direkt verwenden. Sie können sie innerhalb von Klassenkomponenten oder an anderen Stellen verwenden, an denen die Verwendung von Hooks nicht möglich ist.

Die gesamte Anwendung ist vom Context Provider umschlossen, so dass Sie ihn innerhalb einer Klassenkomponente verwenden können, zum Beispiel innerhalb des render-Blocks. Sie können ihn auch innerhalb von Funktionen verwenden, die nicht in funktionalen Komponenten verschachtelt sind.

BreakpointTracker.applyBreakpointOverrides gibt nur ein breakpointProps-Objekt zurück.

Es benötigt zwei Argumente: props und einen breakpoint.

Breakpoint-Kontext in einer funktionalen Komponente​

import React, { useContext } from 'react';
import { BreakpointTracker, BreakpointTrackerContext } from '@jutro/layout';

function BigBanner(props) {
const breakpoint = useContext(BreakpointTrackerContext);
const someRenderFunction = () => {
const breakpointProps = BreakpointTracker.applyBreakpointOverrides(
props,
breakpoint
);
return (
<pre>
<code>{JSON.stringify(breakpointProps, null, 2)}</code>
</pre>
);
};
return <div>{someRenderFunction()}</div>;
}

Breakpoint-Kontext in einer Klassenkomponente​

import React from 'react';
import { BreakpointTracker, BreakpointTrackerContext } from '@jutro/layout';

class ClassBanner extends React.Component {
static contextType = BreakpointTrackerContext;
render() {
const breakpointProps = BreakpointTracker.applyBreakpointOverrides(
this.props,
this.context
);
return (
<pre>
<code>{JSON.stringify(breakpointProps, null, 2)}</code>
</pre>
);
}
}

export default ClassBanner;

Breakpoints in Unit-Tests​

Um Breakpoints zu testen, können Sie das folgende Beispiel als Orientierungshilfe verwenden. Hier wird die BigBanner-Komponente getestet, die wir in anderen Abschnitten dieser Seite erstellt haben.

import React from 'react';
import { render } from '@testing-library/react';
import { BreakpointTrackerContext } from '@jutro/layout';

import { BigBanner } from '../BigBanner';

import styles from '../BigBanner.module.scss';

function renderBigBannerWithBreakpoints(props = {}, breakpoint = 'desktop') {
return render(
<BreakpointTrackerContext.Provider value={breakpoint}>
<BigBanner {...props} />
</BreakpointTrackerContext.Provider>
);
}

describe('BigBanner', () => {
it('renders with default class on desktop', () => {
const { container } = renderBigBannerWithBreakpoints();

expect(container.firstChild).toHaveClass(styles.defaultColor);
});

it('renders with mobile class on phone', () => {
const { container } = renderBigBannerWithBreakpoints({}, 'phone');

expect(container.firstChild).toHaveClass(styles.phoneColor);
});
});