@dhis2-ui/segmented-control 10.16.2 → 10.16.3-alpha.1

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": "@dhis2-ui/segmented-control",
3
- "version": "10.16.2",
3
+ "version": "10.16.3-alpha.1",
4
4
  "description": "UI Segmented Control",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,13 +33,14 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@dhis2/prop-types": "^3.1.2",
36
- "@dhis2/ui-constants": "10.16.2",
36
+ "@dhis2/ui-constants": "10.16.3-alpha.1",
37
37
  "classnames": "^2.3.1",
38
38
  "prop-types": "^15.7.2"
39
39
  },
40
40
  "files": [
41
41
  "build",
42
- "types"
42
+ "types",
43
+ "src"
43
44
  ],
44
45
  "devDependencies": {
45
46
  "react": "^18.3.1",
package/src/index.js ADDED
@@ -0,0 +1 @@
1
+ export { SegmentedControl } from './segmented-control.js'
@@ -0,0 +1,124 @@
1
+ import { colors, elevations, spacers, theme } from '@dhis2/ui-constants'
2
+ import cx from 'classnames'
3
+ import { PropTypes } from 'prop-types'
4
+ import React from 'react'
5
+
6
+ /**
7
+ A segmented control is used to select between options that relate to another
8
+ area of content. All of the options in a segmented control should be closely related.
9
+
10
+ Do not use a segmented control as a standalone selection, it should always be
11
+ used as a selector for other content. For example, do not use a segmented
12
+ control in place of radio buttons when making a single, standalone choice.
13
+
14
+ See specification: [Design System](https://github.com/dhis2/design-system/blob/master/molecules/segmented-control.md)
15
+
16
+ ```js
17
+ import { SegmentedControl } from '@dhis2/ui'
18
+ ```
19
+ */
20
+
21
+ export const SegmentedControl = ({
22
+ options,
23
+ selected,
24
+ onChange,
25
+ ariaLabel,
26
+ }) => {
27
+ if (!options.map(({ value }) => value).includes(selected)) {
28
+ const message =
29
+ `There is no option with the value: "${selected}". ` +
30
+ 'Make sure that the value passed to the selected ' +
31
+ 'prop matches the value of an existing option.'
32
+ throw new Error(message)
33
+ }
34
+
35
+ return (
36
+ <ul className="segmented-control" aria-label={ariaLabel}>
37
+ {options.map(({ label, value, disabled }) => (
38
+ <li key={`option-${value}`}>
39
+ <button
40
+ type="button"
41
+ className={cx('segment', {
42
+ selected: value === selected,
43
+ disabled,
44
+ })}
45
+ onClick={(e) => onChange({ value }, e)}
46
+ disabled={disabled}
47
+ >
48
+ {label}
49
+ </button>
50
+ </li>
51
+ ))}
52
+
53
+ <style jsx>{`
54
+ .segmented-control {
55
+ all: unset;
56
+ list-style: none;
57
+ display: inline-flex;
58
+ align-items: stretch;
59
+ background: ${colors.grey300};
60
+ border-radius: 5px;
61
+ padding: 2px;
62
+ }
63
+
64
+ .segment {
65
+ all: unset;
66
+ box-sizing: border-box;
67
+ cursor: pointer;
68
+ font-size: 14px;
69
+ text-align: center;
70
+ border-radius: 5px;
71
+ background: transparent;
72
+ color: ${colors.grey700};
73
+ min-width: 72px;
74
+ max-width: 320px;
75
+ height: 100%;
76
+ padding: 6px ${spacers.dp12};
77
+ }
78
+
79
+ .segment:focus {
80
+ outline: 3px solid ${theme.focus};
81
+ outline-offset: -3px;
82
+ }
83
+ /*focus-visible backwards compatibility for safari: https://css-tricks.com/platform-news-using-focus-visible-bbcs-new-typeface-declarative-shadow-doms-a11y-and-placeholders/*/
84
+ .segment:focus:not(:focus-visible) {
85
+ outline: none;
86
+ }
87
+
88
+ .segment:not(.selected):not(.disabled):hover {
89
+ background: ${colors.grey400};
90
+ color: ${colors.grey900};
91
+ }
92
+
93
+ .segment.selected {
94
+ cursor: default;
95
+ box-shadow: ${elevations.e100};
96
+ background: ${colors.white};
97
+ color: ${colors.grey900};
98
+ }
99
+
100
+ .segment.disabled {
101
+ cursor: not-allowed;
102
+ opacity: 0.5;
103
+ }
104
+ `}</style>
105
+ </ul>
106
+ )
107
+ }
108
+
109
+ SegmentedControl.propTypes = {
110
+ /** Options to populate the segmented control */
111
+ options: PropTypes.arrayOf(
112
+ PropTypes.shape({
113
+ label: PropTypes.string.isRequired,
114
+ value: PropTypes.string.isRequired,
115
+ disabled: PropTypes.bool,
116
+ })
117
+ ).isRequired,
118
+ /** An option to select; should match the `value` property of the option to be selected */
119
+ selected: PropTypes.string.isRequired,
120
+ /** Called with the signature `({ value: string }, event)` */
121
+ onChange: PropTypes.func.isRequired,
122
+ /** Used to provide an accessible label to a segmented control without a visible label */
123
+ ariaLabel: PropTypes.string,
124
+ }
@@ -0,0 +1,49 @@
1
+ import React from 'react'
2
+ import { SegmentedControl } from './segmented-control.js'
3
+
4
+ const logger = ({ value }) => console.log(`Selected value: ${value}`)
5
+
6
+ export default {
7
+ title: 'Segmented Control',
8
+ component: SegmentedControl,
9
+ parameters: {
10
+ componentSubtitle: 'Allows selection between related options',
11
+ },
12
+ args: {
13
+ options: [
14
+ { label: 'Dog', value: 'DOG' },
15
+ { label: 'Cat', value: 'CAT' },
16
+ { label: 'Giraffe', value: 'GIRAFFE' },
17
+ ],
18
+ selected: 'DOG',
19
+ onChange: logger,
20
+ ariaLabel: 'Segmented control label',
21
+ },
22
+ }
23
+
24
+ const Template = (args) => <SegmentedControl {...args} />
25
+
26
+ export const Default = Template.bind({})
27
+
28
+ export const DisabledOption = Template.bind({})
29
+ DisabledOption.args = {
30
+ options: [
31
+ { label: 'One', value: 'ONE' },
32
+ { label: 'Two', value: 'TWO', disabled: true },
33
+ { label: 'Three', value: 'THREE' },
34
+ ],
35
+ selected: 'THREE',
36
+ }
37
+
38
+ export const LongLabels = Template.bind({})
39
+ LongLabels.args = {
40
+ options: [
41
+ {
42
+ label: 'Program configuration, security, and distribution',
43
+ value: 'PROGRAM',
44
+ },
45
+ { label: 'Two', value: 'TWO' },
46
+ { label: 'Three', value: 'THREE' },
47
+ ],
48
+ selected: 'PROGRAM',
49
+ }