Skip to content

Commit 9d97e9e

Browse files
authored
docs(VariantManagement): add FilterBar example (#4857)
1 parent e3b20a5 commit 9d97e9e

File tree

2 files changed

+479
-2
lines changed

2 files changed

+479
-2
lines changed

packages/main/src/components/VariantManagement/VariantManagement.mdx

Lines changed: 241 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ These props accept props of the `Input` component with which you are able to ove
3232

3333
<Canvas of={ComponentStories.WithCustomValidation} sourceState="none" />
3434

35-
**Code Example**
35+
### Code Example
3636

3737
<details style={{ marginBlockEnd: "2rem" }}>
3838

@@ -114,6 +114,246 @@ export const VariantManagementWithCustomValidation = () => {
114114

115115
</details>
116116

117+
## Basic VariantManagement Implementation
118+
119+
This example shows how a basic implementation of the `VariantManagement` inside a `FilterBar` could look like. For a more in depth documentation you can take a look at the official [Fiori for Web Design Guidelines](https://experience.sap.com/fiori-design-web/variant-management/).
120+
121+
**Note:** All views are applied automatically, so the "Apply Automatically" checkboxes in both dialogs won't be visible.
122+
123+
<Canvas of={ComponentStories.WithFilterBarImplementation} sourceState="none" />
124+
125+
### Code Example
126+
127+
<details style={{ marginBlockEnd: "2rem" }}>
128+
129+
<summary>Show Code</summary>
130+
131+
```tsx
132+
function FilterBarWithVariantManagement() {
133+
const [selectedVariant, setSelectedVariant] = useState('Standard');
134+
const [defaultVariant, setDefaultVariant] = useState('Standard');
135+
const [customVariants, setCustomVariants] = useState<VariantItemPropTypes[]>([]);
136+
const [isDirty, setIsDirty] = useState(false);
137+
const [checkIfDiry, setCheckIfDirty] = useState(false);
138+
139+
// this should be persisted (e.g. by the local storage of the browser or on the server)
140+
const initialVariantValues = useRef({
141+
Standard: {
142+
selectedCountry: 'Indonesia',
143+
date: '',
144+
selectedCodes: {}
145+
}
146+
});
147+
148+
const filterReducer = (state, action) => {
149+
const { payload, type } = action;
150+
setCheckIfDirty(true);
151+
switch (type) {
152+
case 'selectedCountry':
153+
return { ...state, selectedCountry: payload };
154+
case 'date':
155+
return { ...state, date: payload };
156+
case 'selectedCodes':
157+
return { ...state, selectedCodes: payload };
158+
case 'changeVariant':
159+
return payload;
160+
default:
161+
console.warn('Unknown action type!');
162+
return state;
163+
}
164+
};
165+
166+
const [filters, dispatchFiltersChange] = useReducer(filterReducer, initialVariantValues.current.Standard);
167+
const { selectedCountry, date, selectedCodes } = filters;
168+
169+
const handleSelectChange = (e) => {
170+
const { selectedOption } = e.detail;
171+
dispatchFiltersChange({ type: 'selectedCountry', payload: selectedOption.textContent });
172+
};
173+
const handleDateChange = (e) => {
174+
dispatchFiltersChange({ type: 'date', payload: e.detail.value });
175+
};
176+
const handleSelectedCodesChange = (e) => {
177+
dispatchFiltersChange({
178+
type: 'selectedCodes',
179+
payload: e.detail.items.reduce((acc, cur) => {
180+
acc[cur.dataset.code] = true;
181+
return acc;
182+
}, {})
183+
});
184+
};
185+
186+
const handleSaveAs = (e) => {
187+
const {
188+
variantItem: _omit,
189+
selected: _omit2,
190+
readOnly: _omit3,
191+
labelReadOnly: _omit4,
192+
...variantItemProps
193+
} = e.detail;
194+
if (variantItemProps.isDefault) {
195+
setDefaultVariant(variantItemProps.children);
196+
}
197+
setCustomVariants((prev) => [...prev, { ...variantItemProps, author: 'Current User' }]);
198+
initialVariantValues.current[variantItemProps.children] = filters;
199+
setSelectedVariant(variantItemProps.children);
200+
setIsDirty(false);
201+
};
202+
203+
const handleSave = (e) => {
204+
const { variantItem: _omit, selected: _omit2, ...variantItemProps } = e.detail;
205+
initialVariantValues.current[variantItemProps.children] = filters;
206+
setIsDirty(false);
207+
};
208+
209+
const handleSaveManageViews = (e) => {
210+
const { deletedVariants, updatedVariants } = e.detail;
211+
deletedVariants.forEach((variant) => {
212+
delete initialVariantValues.current[variant.children];
213+
setCustomVariants((prev) => prev.filter((item) => item.children !== variant.children));
214+
});
215+
updatedVariants.forEach((variant) => {
216+
const { variantItem: _omit, selected: _omit2, ...variantItemProps } = variant;
217+
initialVariantValues.current[variant.children] = variantItemProps;
218+
if (variant.isDefault) {
219+
setDefaultVariant(variant.children);
220+
}
221+
if (variant.selected) {
222+
setSelectedVariant(variant.children);
223+
}
224+
setCustomVariants((prev) => {
225+
return prev.map((item) => {
226+
if (item.children === variant.prevVariant.children) {
227+
return variantItemProps;
228+
}
229+
return item;
230+
});
231+
});
232+
});
233+
};
234+
235+
const handleSelect = (e) => {
236+
const privateSelectedVariant = e.detail.selectedVariant.children;
237+
dispatchFiltersChange({
238+
type: 'changeVariant',
239+
payload: initialVariantValues.current[privateSelectedVariant]
240+
});
241+
setSelectedVariant(privateSelectedVariant);
242+
};
243+
244+
useEffect(() => {
245+
if (checkIfDiry) {
246+
const hasChanged = Object.entries(initialVariantValues.current[selectedVariant]).some(([key, val]) => {
247+
if (key === 'selectedCodes') {
248+
const selectedCodesLength = Object.keys(filters.selectedCodes).length;
249+
if (selectedCodesLength > 0 && Object.keys(val).length !== selectedCodesLength) {
250+
return true;
251+
}
252+
return Object.entries(filters.selectedCodes).some(([code, bool]) => {
253+
return val[code] !== bool;
254+
});
255+
}
256+
return filters[key] !== val;
257+
});
258+
setCheckIfDirty(false);
259+
setIsDirty(hasChanged);
260+
}
261+
}, [checkIfDiry, selectedVariant]);
262+
263+
return (
264+
<>
265+
<FilterBar
266+
header={
267+
<VariantManagement
268+
hideApplyAutomatically
269+
dirtyState={isDirty}
270+
onSaveAs={handleSaveAs}
271+
onSave={handleSave}
272+
onSelect={handleSelect}
273+
onSaveManageViews={handleSaveManageViews}
274+
>
275+
<VariantItem
276+
selected={selectedVariant === 'Standard'}
277+
global
278+
isDefault={defaultVariant === 'Standard'}
279+
author="SAP"
280+
readOnly
281+
labelReadOnly
282+
>
283+
Standard
284+
</VariantItem>
285+
{customVariants.map((variantItemProps) => {
286+
return (
287+
<VariantItem
288+
key={variantItemProps.children}
289+
{...variantItemProps}
290+
selected={selectedVariant === variantItemProps.children}
291+
isDefault={defaultVariant === variantItemProps.children}
292+
>
293+
{variantItemProps.children}
294+
</VariantItem>
295+
);
296+
})}
297+
</VariantManagement>
298+
}
299+
>
300+
<FilterGroupItem label="Countries">
301+
<Select onChange={handleSelectChange}>
302+
<Option selected={selectedCountry === 'Indonesia'}>Indonesia</Option>
303+
<Option selected={selectedCountry === 'Costa Rica'}>Costa Rica</Option>
304+
<Option selected={selectedCountry === 'Slovakia'}>Slovakia</Option>
305+
<Option selected={selectedCountry === 'Iceland'}>Iceland</Option>
306+
<Option selected={selectedCountry === 'Malta'}>Malta</Option>
307+
<Option selected={selectedCountry === 'Guyana'}>Guyana</Option>
308+
<Option selected={selectedCountry === 'Spain'}>Spain</Option>
309+
<Option selected={selectedCountry === 'Austria'}>Austria</Option>
310+
</Select>
311+
</FilterGroupItem>
312+
<FilterGroupItem label="Date">
313+
<DatePicker value={date} onChange={handleDateChange} />
314+
</FilterGroupItem>
315+
<FilterGroupItem label="Company Code">
316+
<MultiComboBox onSelectionChange={handleSelectedCodesChange}>
317+
<MultiComboBoxItem text="001" selected={selectedCodes['001']} data-code="001" />
318+
<MultiComboBoxItem text="002" selected={selectedCodes['002']} data-code="002" />
319+
<MultiComboBoxItem text="003" selected={selectedCodes['003']} data-code="003" />
320+
<MultiComboBoxItem text="004" selected={selectedCodes['004']} data-code="004" />
321+
<MultiComboBoxItem text="005" selected={selectedCodes['005']} data-code="005" />
322+
</MultiComboBox>
323+
</FilterGroupItem>
324+
</FilterBar>
325+
<Form
326+
style={{ marginBlockStart: '2rem' }}
327+
columnsS={1}
328+
columnsM={1}
329+
columnsL={1}
330+
columnsXL={1}
331+
labelSpanM={2}
332+
labelSpanL={2}
333+
labelSpanXL={2}
334+
>
335+
<FormItem label="Current View">
336+
<Text>{selectedVariant}</Text>
337+
</FormItem>
338+
<FormGroup titleText="Filters">
339+
<FormItem label="Selected Country">
340+
<Text>{selectedCountry}</Text>
341+
</FormItem>
342+
<FormItem label="Selected Date">
343+
<Text>{date}</Text>
344+
</FormItem>
345+
<FormItem label="Selected Company Codes">
346+
<Text>{Object.keys(selectedCodes).join(', ')}</Text>
347+
</FormItem>
348+
</FormGroup>
349+
</Form>
350+
</>
351+
);
352+
}
353+
```
354+
355+
</details>
356+
117357
<Markdown>{SubcomponentsSection}</Markdown>
118358

119359
## VariantItem

0 commit comments

Comments
 (0)