@centreon/ui 26.12.5 → 26.12.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -110,3 +110,42 @@ export const exclusiveCheckboxGroup = (): JSX.Element => (
|
|
|
110
110
|
validationSchema={object()}
|
|
111
111
|
/>
|
|
112
112
|
);
|
|
113
|
+
|
|
114
|
+
export const segmentedButtons = (): JSX.Element => (
|
|
115
|
+
<Form
|
|
116
|
+
initialValues={{
|
|
117
|
+
activeChecks: 'use_default',
|
|
118
|
+
updateMode: 'incremental'
|
|
119
|
+
}}
|
|
120
|
+
inputs={[
|
|
121
|
+
{
|
|
122
|
+
fieldName: 'activeChecks',
|
|
123
|
+
group: '',
|
|
124
|
+
label: 'Active checks enabled',
|
|
125
|
+
segmentedButtons: {
|
|
126
|
+
options: [
|
|
127
|
+
{ label: 'Yes', value: 'true' },
|
|
128
|
+
{ label: 'No', value: 'false' },
|
|
129
|
+
{ label: 'Default', value: 'use_default' }
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
type: InputType.SegmentedButtons
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
fieldName: 'updateMode',
|
|
136
|
+
group: '',
|
|
137
|
+
label: 'Update mode (long labels)',
|
|
138
|
+
segmentedButtons: {
|
|
139
|
+
options: [
|
|
140
|
+
{ label: 'Incremental', value: 'incremental' },
|
|
141
|
+
{ label: 'Replacement', value: 'replacement' },
|
|
142
|
+
{ label: 'Inherit from the host template', value: 'inherit' }
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
type: InputType.SegmentedButtons
|
|
146
|
+
}
|
|
147
|
+
]}
|
|
148
|
+
submit={submit}
|
|
149
|
+
validationSchema={object()}
|
|
150
|
+
/>
|
|
151
|
+
);
|
|
@@ -27,38 +27,44 @@ const ValueDisplay = (): JSX.Element => {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
interface InitializeProps {
|
|
30
|
+
columnWidth?: number;
|
|
30
31
|
disabled?: boolean;
|
|
31
32
|
enabled?: string;
|
|
33
|
+
segmentedOptions?: Array<{ label: string; value: string }>;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
const initialize = ({
|
|
35
37
|
enabled = 'use_default',
|
|
36
|
-
disabled = false
|
|
38
|
+
disabled = false,
|
|
39
|
+
columnWidth = 800,
|
|
40
|
+
segmentedOptions = options
|
|
37
41
|
}: InitializeProps = {}): void => {
|
|
38
42
|
render(
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
{
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
43
|
+
<div style={{ width: columnWidth }}>
|
|
44
|
+
<Form
|
|
45
|
+
initialValues={{ enabled }}
|
|
46
|
+
inputs={[
|
|
47
|
+
{
|
|
48
|
+
dataTestId: 'enabled',
|
|
49
|
+
fieldName: 'enabled',
|
|
50
|
+
getDisabled: () => disabled,
|
|
51
|
+
group: '',
|
|
52
|
+
label: 'Notification Enabled',
|
|
53
|
+
segmentedButtons: { options: segmentedOptions },
|
|
54
|
+
type: InputType.SegmentedButtons
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
custom: { Component: ValueDisplay },
|
|
58
|
+
fieldName: 'value',
|
|
59
|
+
group: '',
|
|
60
|
+
label: 'value',
|
|
61
|
+
type: InputType.Custom
|
|
62
|
+
}
|
|
63
|
+
]}
|
|
64
|
+
submit={rs.fn()}
|
|
65
|
+
validationSchema={object()}
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
62
68
|
);
|
|
63
69
|
};
|
|
64
70
|
|
|
@@ -104,6 +110,61 @@ describe('Segmented buttons', () => {
|
|
|
104
110
|
expect(await button('No')).toHaveAttribute('aria-pressed', 'true');
|
|
105
111
|
});
|
|
106
112
|
|
|
113
|
+
it('sizes to its labels, not to its column', async () => {
|
|
114
|
+
initialize({ columnWidth: 800 });
|
|
115
|
+
|
|
116
|
+
const group = await screen.findByTestId('enabled');
|
|
117
|
+
const widths = (
|
|
118
|
+
await Promise.all(['Yes', 'No', 'Default'].map(button))
|
|
119
|
+
).map((element) => element.getBoundingClientRect().width);
|
|
120
|
+
|
|
121
|
+
// Three equal segments of at least 104px (26 spacing units), left aligned in the column
|
|
122
|
+
// (a pixel apart at most: the group overlaps the borders of its buttons).
|
|
123
|
+
expect(Math.max(...widths) - Math.min(...widths)).toBeLessThanOrEqual(1);
|
|
124
|
+
expect(Math.min(...widths)).toBeGreaterThanOrEqual(104);
|
|
125
|
+
expect(group.getBoundingClientRect().width).toBeLessThan(400);
|
|
126
|
+
expect(group.getBoundingClientRect().left).toBe(
|
|
127
|
+
(await screen.findByText('Notification Enabled')).getBoundingClientRect()
|
|
128
|
+
.left
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('stays inside a column narrower than its segments', async () => {
|
|
133
|
+
initialize({ columnWidth: 200 });
|
|
134
|
+
|
|
135
|
+
const group = await screen.findByTestId('enabled');
|
|
136
|
+
const wrapper = group.parentElement as HTMLElement;
|
|
137
|
+
|
|
138
|
+
expect(wrapper.getBoundingClientRect().width).toBeLessThanOrEqual(200);
|
|
139
|
+
expect(wrapper.scrollWidth).toBeGreaterThan(wrapper.clientWidth);
|
|
140
|
+
expect((await button('Default')).scrollWidth).toBeLessThanOrEqual(
|
|
141
|
+
(await button('Default')).clientWidth
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('widens every segment to the longest label, without wrapping', async () => {
|
|
146
|
+
const longLabel = 'Inherit from the host template';
|
|
147
|
+
|
|
148
|
+
initialize({
|
|
149
|
+
segmentedOptions: [
|
|
150
|
+
{ label: 'Yes', value: 'true' },
|
|
151
|
+
{ label: 'No', value: 'false' },
|
|
152
|
+
{ label: longLabel, value: 'use_default' }
|
|
153
|
+
]
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const yes = await button('Yes');
|
|
157
|
+
const long = await button(longLabel);
|
|
158
|
+
|
|
159
|
+
expect(
|
|
160
|
+
Math.abs(
|
|
161
|
+
yes.getBoundingClientRect().width - long.getBoundingClientRect().width
|
|
162
|
+
)
|
|
163
|
+
).toBeLessThanOrEqual(1);
|
|
164
|
+
expect(long.scrollWidth).toBeLessThanOrEqual(long.clientWidth);
|
|
165
|
+
expect(long.getBoundingClientRect().height).toBe(28);
|
|
166
|
+
});
|
|
167
|
+
|
|
107
168
|
it('does not change when disabled', async () => {
|
|
108
169
|
initialize({ disabled: true });
|
|
109
170
|
|
|
@@ -33,35 +33,39 @@ const SegmentedButtons = ({
|
|
|
33
33
|
Component: (
|
|
34
34
|
<FormGroup>
|
|
35
35
|
<FormLabel>{t(label)}</FormLabel>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
{/* In a column narrower than the segments, the group scrolls inside it
|
|
37
|
+
rather than overflowing into what is next to it. */}
|
|
38
|
+
<div className="max-w-full self-start overflow-x-auto">
|
|
39
|
+
<ButtonGroup
|
|
40
|
+
aria-label={t(label)}
|
|
41
|
+
// Equal segments, each as wide as the longest label (26 spacing
|
|
42
|
+
// units, 104px, at least), whatever the width of the column.
|
|
43
|
+
className="inline-grid auto-cols-[minmax(calc(var(--spacing)*26),1fr)] grid-flow-col"
|
|
44
|
+
color="primary"
|
|
45
|
+
data-testid={dataTestId}
|
|
46
|
+
disabled={disabled}
|
|
47
|
+
size="small"
|
|
48
|
+
>
|
|
49
|
+
{segmentedButtons?.options.map(
|
|
50
|
+
({ label: optionLabel, value: optionValue }) => {
|
|
51
|
+
const isSelected = equals(value, optionValue);
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
return (
|
|
54
|
+
<Button
|
|
55
|
+
aria-pressed={isSelected}
|
|
56
|
+
className="h-7 whitespace-nowrap px-3 py-0 font-medium text-sm leading-[21px] normal-case"
|
|
57
|
+
data-testid={`${dataTestId}-${optionValue}`}
|
|
58
|
+
key={optionValue}
|
|
59
|
+
onClick={select(optionValue)}
|
|
60
|
+
variant={isSelected ? 'contained' : 'outlined'}
|
|
61
|
+
>
|
|
62
|
+
{t(optionLabel)}
|
|
63
|
+
</Button>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
)}
|
|
67
|
+
</ButtonGroup>
|
|
68
|
+
</div>
|
|
65
69
|
</FormGroup>
|
|
66
70
|
),
|
|
67
71
|
memoProps: [value, disabled, segmentedButtons, additionalMemoProps]
|