@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centreon/ui",
3
- "version": "26.12.5",
3
+ "version": "26.12.6",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "update:deps": "pnpx npm-check-updates -i --format group",
@@ -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
- <Form
40
- initialValues={{ enabled }}
41
- inputs={[
42
- {
43
- dataTestId: 'enabled',
44
- fieldName: 'enabled',
45
- getDisabled: () => disabled,
46
- group: '',
47
- label: 'Notification Enabled',
48
- segmentedButtons: { options },
49
- type: InputType.SegmentedButtons
50
- },
51
- {
52
- custom: { Component: ValueDisplay },
53
- fieldName: 'value',
54
- group: '',
55
- label: 'value',
56
- type: InputType.Custom
57
- }
58
- ]}
59
- submit={rs.fn()}
60
- validationSchema={object()}
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
- <ButtonGroup
37
- aria-label={t(label)}
38
- // As wide as its column, up to what three labels need.
39
- className="max-w-[412px]"
40
- color="primary"
41
- data-testid={dataTestId}
42
- disabled={disabled}
43
- fullWidth
44
- size="small"
45
- >
46
- {segmentedButtons?.options.map(
47
- ({ label: optionLabel, value: optionValue }) => {
48
- const isSelected = equals(value, optionValue);
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
- return (
51
- <Button
52
- aria-pressed={isSelected}
53
- className="h-7 px-3 py-0 font-medium text-sm leading-[21px] normal-case"
54
- data-testid={`${dataTestId}-${optionValue}`}
55
- key={optionValue}
56
- onClick={select(optionValue)}
57
- variant={isSelected ? 'contained' : 'outlined'}
58
- >
59
- {t(optionLabel)}
60
- </Button>
61
- );
62
- }
63
- )}
64
- </ButtonGroup>
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]