Skip to content

Commit 19ac8c6

Browse files
authored
fix(time-input): readonly fix (#724)
* fix(time-input): readonly fix
1 parent bb958f4 commit 19ac8c6

File tree

5 files changed

+167
-63
lines changed

5 files changed

+167
-63
lines changed

src/components/inputs/time-input/time-input-body.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ const getIconTheme = (isDisabled, isMouseOver) => {
1919

2020
export const ClearSection = props => (
2121
<div
22-
onClick={props.isDisabled ? undefined : props.onClear}
23-
css={getClearSectionStyles(props)}
22+
onClick={props.isDisabled || props.isReadOnly ? undefined : props.onClear}
23+
css={theme => getClearSectionStyles(props, theme)}
2424
onMouseOver={props.handleMouseOver}
2525
onMouseOut={props.handleMouseOut}
2626
>
@@ -35,6 +35,7 @@ export const ClearSection = props => (
3535
ClearSection.displayName = 'ClearSection';
3636
ClearSection.propTypes = {
3737
isDisabled: PropTypes.bool,
38+
isReadOnly: PropTypes.bool,
3839
hasError: PropTypes.bool,
3940
isMouseOver: PropTypes.bool.isRequired,
4041
onClear: PropTypes.func,
@@ -74,27 +75,31 @@ export default class TimeInputBody extends React.Component {
7475
id={this.props.id}
7576
name={this.props.name}
7677
autoComplete={this.props.autoComplete}
77-
css={getTimeInputStyles(this.props)}
78+
css={theme => getTimeInputStyles(this.props, theme)}
7879
placeholder={this.props.placeholder}
7980
autoFocus={this.props.isAutofocussed}
8081
disabled={this.props.isDisabled}
82+
readOnly={this.props.isReadOnly}
8183
value={this.props.value}
8284
onChange={this.props.onChange}
8385
onFocus={this.props.onFocus}
8486
onBlur={this.props.onBlur}
8587
{...filterDataAttributes(this.props)}
8688
/* ARIA */
8789
role="textbox"
90+
aria-readonly={this.props.isReadOnly}
91+
contentEditable={!this.props.isReadOnly}
8892
/>
8993
<ClearSectionWithMouseOverState
9094
isDisabled={this.props.isDisabled}
9195
hasError={this.props.hasError}
96+
isReadOnly={this.props.isReadOnly}
9297
onClear={this.props.onClear}
9398
/>
9499
<label
95100
htmlFor={this.props.id}
96101
data-toggle
97-
css={getClockIconContainerStyles(this.props)}
102+
css={theme => getClockIconContainerStyles(this.props, theme)}
98103
>
99104
<ClockIcon theme={this.props.isDisabled ? 'grey' : 'black'} />
100105
</label>

src/components/inputs/time-input/time-input-body.styles.js

+141-58
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,205 @@
11
import { css } from '@emotion/core';
22
import vars from '../../../../materials/custom-properties';
3+
import designTokens from '../../../../materials/design-tokens';
4+
35
import { getInputStyles } from '../styles';
46

57
// NOTE: order is important here
68
// * a disabled-field currently does not display warning/error-states so it takes precedence
79
// * a readonly-field cannot be changed, but it might be relevant for validation, so error and warning are checked first
810
// how you can interact with the field is controlled separately by the props, this only influences visuals
9-
const getClearSectionStyles = props => {
11+
const getClearSectionStyles = (props, theme) => {
12+
const overwrittenVars = {
13+
...vars,
14+
...theme,
15+
};
16+
1017
const baseIconStyles = css`
1118
align-items: center;
1219
box-sizing: border-box;
13-
background-color: ${vars.backgroundColorInputPristine};
14-
border-bottom: 1px solid ${vars.borderColorInputPristine};
15-
border-right: 1px solid ${vars.borderColorInputPristine};
16-
border-top: 1px solid ${vars.borderColorInputPristine};
20+
background-color: ${overwrittenVars[designTokens.backgroundColorForInput]};
21+
border-bottom: 1px solid
22+
${overwrittenVars[designTokens.borderColorForInput]};
23+
border-right: 1px solid ${overwrittenVars[designTokens.borderColorForInput]};
24+
border-top: 1px solid ${overwrittenVars[designTokens.borderColorForInput]};
1725
border-left: none;
18-
height: ${vars.sizeHeightInput};
26+
height: ${overwrittenVars.sizeHeightInput};
1927
display: flex;
20-
padding: ${vars.spacingXs};
28+
padding: ${overwrittenVars.spacingXs};
29+
transition: ${overwrittenVars.transitionStandard};
2130
cursor: pointer;
2231
`;
2332
if (props.isDisabled) {
2433
return [
2534
baseIconStyles,
2635
css`
2736
cursor: not-allowed;
28-
background-color: ${vars.backgroundColorInputDisabled};
29-
color: ${vars.fontColorDisabled};
30-
border-color: ${vars.borderColorInputDisabled};
37+
background-color: ${overwrittenVars[
38+
designTokens.backgroundColorForInputWhenDisabled
39+
]};
40+
color: ${overwrittenVars[designTokens.fontColorForInputWhenDisabled]};
41+
border-color: ${overwrittenVars[
42+
designTokens.borderColorForInputWhenDisabled
43+
]};
44+
`,
45+
];
46+
}
47+
if (props.isReadOnly) {
48+
return [
49+
baseIconStyles,
50+
css`
51+
cursor: default;
52+
color: ${overwrittenVars[designTokens.fontColorForInputWhenReadonly]};
53+
border-color: ${overwrittenVars[
54+
designTokens.borderColorForInputWhenReadonly
55+
]};
56+
57+
svg path {
58+
fill: ${overwrittenVars[designTokens.fontColorForInputWhenReadonly]};
59+
}
3160
`,
3261
];
3362
}
3463
if (props.hasError) {
3564
return [
3665
baseIconStyles,
3766
css`
38-
color: ${vars.fontColorError};
39-
border-color: ${vars.borderColorInputError};
67+
color: ${overwrittenVars[designTokens.fontColorForInputWhenError]};
68+
border-color: ${overwrittenVars[
69+
designTokens.borderColorForInputWhenError
70+
]};
4071
`,
4172
];
4273
}
4374
return baseIconStyles;
4475
};
4576

46-
const getClockIconContainerStyles = props => {
77+
const getClockIconContainerStyles = (props, theme) => {
78+
const overwrittenVars = {
79+
...vars,
80+
...theme,
81+
};
82+
4783
const baseIconStyles = css`
4884
align-items: center;
4985
box-sizing: border-box;
50-
background-color: ${vars.backgroundColorInputPristine};
51-
border-bottom: 1px solid ${vars.borderColorInputPristine};
52-
border-right: 1px solid ${vars.borderColorInputPristine};
53-
border-top: 1px solid ${vars.borderColorInputPristine};
86+
background-color: ${overwrittenVars[designTokens.backgroundColorForInput]};
87+
border-bottom: 1px solid
88+
${overwrittenVars[designTokens.borderColorForInput]};
89+
border-right: 1px solid ${overwrittenVars[designTokens.borderColorForInput]};
90+
border-top: 1px solid ${overwrittenVars[designTokens.borderColorForInput]};
5491
border-left: none;
55-
height: ${vars.sizeHeightInput};
92+
height: ${overwrittenVars.sizeHeightInput};
5693
display: flex;
57-
padding: ${vars.spacingXs};
58-
border-top-right-radius: ${vars.borderRadiusInput};
59-
border-bottom-right-radius: ${vars.borderRadiusInput};
94+
padding: ${overwrittenVars.spacingXs};
95+
border-top-right-radius: ${overwrittenVars[
96+
designTokens.borderRadiusForInput
97+
]};
98+
border-bottom-right-radius: ${overwrittenVars[
99+
designTokens.borderRadiusForInput
100+
]};
60101
`;
61102
if (props.isDisabled) {
62103
return [
63104
baseIconStyles,
64105
css`
65106
cursor: not-allowed;
66-
background-color: ${vars.backgroundColorInputDisabled};
67-
color: ${vars.fontColorDisabled};
68-
border-color: ${vars.borderColorInputDisabled};
107+
background-color: ${overwrittenVars[
108+
designTokens.backgroundColorForInputWhenDisabled
109+
]};
110+
color: ${overwrittenVars[designTokens.fontColorForInputWhenDisabled]};
111+
border-color: ${overwrittenVars[
112+
designTokens.borderColorForInputWhenDisabled
113+
]};
114+
`,
115+
];
116+
}
117+
if (props.isReadOnly) {
118+
return [
119+
baseIconStyles,
120+
css`
121+
cursor: default;
122+
color: ${overwrittenVars[designTokens.fontColorForInputWhenReadonly]};
123+
border-color: ${overwrittenVars[
124+
designTokens.borderColorForInputWhenReadonly
125+
]};
126+
127+
svg path {
128+
fill: ${overwrittenVars[designTokens.fontColorForInputWhenReadonly]};
129+
}
69130
`,
70131
];
71132
}
72133
if (props.hasError) {
73134
return [
74135
baseIconStyles,
75136
css`
76-
color: ${vars.fontColorError};
77-
border-color: ${vars.borderColorInputError};
137+
color: ${overwrittenVars[designTokens.fontColorFolorInputWhenError]};
138+
border-color: ${overwrittenVars[
139+
designTokens.borderColorForInputWhenError
140+
]};
78141
`,
79142
];
80143
}
81144
return baseIconStyles;
82145
};
83146

84-
const getInputContainerStyles = () => css`
85-
width: 100%;
86-
align-items: center;
87-
display: flex;
88-
font-size: ${vars.fontSizeDefault};
89-
font-family: ${vars.fontFamilyDefault};
90-
`;
147+
const getInputContainerStyles = (props, theme) => {
148+
const overwrittenVars = {
149+
...props,
150+
...theme,
151+
};
152+
153+
return css`
154+
width: 100%;
155+
align-items: center;
156+
display: flex;
157+
font-size: ${overwrittenVars[designTokens.fontSizeForInput]};
158+
font-family: ${overwrittenVars[designTokens.fontSizeForInput]};
159+
`;
160+
};
161+
162+
const getTimeInputStyles = (props, theme) => {
163+
const overwrittenVars = {
164+
...props,
165+
...theme,
166+
};
91167

92-
const getTimeInputStyles = props => [
93-
getInputStyles(props),
94-
css`
95-
border-radius: ${vars.borderRadiusInput} 0 0 ${vars.borderRadiusInput};
96-
border-right: none;
168+
return [
169+
getInputStyles(props, theme),
170+
css`
171+
border-radius: ${overwrittenVars[designTokens.borderRadiusInput]} 0 0
172+
${overwrittenVars[designTokens.borderRadiusInput]};
173+
border-right: none;
97174
98-
&:focus,
99-
&:active,
100-
&:focus + *,
101-
&:active + * {
102-
border-color: ${vars.borderColorInputFocus};
103-
color: ${vars.fontColorDefault};
104-
transition: ${vars.transitionStandard};
105-
}
175+
&:focus,
176+
&:active,
177+
&:focus + *,
178+
&:active + * {
179+
border-color: ${overwrittenVars[
180+
designTokens.borderColorForInputWhenFocused
181+
]};
182+
color: ${overwrittenVars[designTokens.fontColorForInput]};
183+
transition: ${overwrittenVars.transitionStandard};
184+
}
106185
107-
&:disabled {
108-
cursor: not-allowed;
109-
}
186+
&:disabled {
187+
cursor: not-allowed;
188+
}
110189
111-
&:disabled,
112-
&:read-only {
113-
background-color: ${vars.backgroundColorInputDisabled};
114-
color: ${vars.fontColorDisabled};
115-
border-color: ${vars.borderColorInputDisabled};
116-
opacity: 1; /* fix for mobile safari */
117-
}
118-
`,
119-
];
190+
&:disabled {
191+
background-color: ${overwrittenVars[
192+
designTokens.backgroundColorForInputWhenDisabled
193+
]};
194+
color: ${overwrittenVars[designTokens.fontColorForInputWhenDisabled]};
195+
border-color: ${overwrittenVars[
196+
designTokens.borderColorForInputWhenDisabled
197+
]};
198+
opacity: 1; /* fix for mobile safari */
199+
}
200+
`,
201+
];
202+
};
120203

121204
export {
122205
getClearSectionStyles,

src/components/inputs/time-input/time-input.js

+1
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ export class TimeInput extends React.Component {
149149
isAutofocussed={this.props.isAutofocussed}
150150
isDisabled={this.props.isDisabled}
151151
hasError={this.props.hasError}
152+
isReadOnly={this.props.isReadOnly}
152153
onClear={() => this.emitChange('')}
153154
placeholder={
154155
typeof this.props.placeholder === 'string'

src/components/inputs/time-input/time-input.story.js

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ storiesOf('Components|Inputs', module)
2323
placeholder={text('placeholder', 'Enter time')}
2424
isAutofocussed={boolean('isAutofocussed', false)}
2525
isDisabled={boolean('isDisabled', false)}
26+
isReadOnly={boolean('isReadOnly', false)}
2627
value={text('value', value)}
2728
onChange={event => {
2829
action('onChange')(event);

src/components/inputs/time-input/time-input.visualroute.js

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import React from 'react';
22
import { TimeInput } from 'ui-kit';
3+
import { ThemeProvider } from 'emotion-theming';
34
import { Suite, Spec } from '../../../../test/percy';
45

56
const value = '3:00 PM';
67

78
export const routePath = '/time-input';
89

9-
export const component = () => (
10+
export const component = ({ themes }) => (
1011
<Suite>
1112
<Spec label="minimal">
1213
<TimeInput value={value} onChange={() => {}} horizontalConstraint="m" />
@@ -35,5 +36,18 @@ export const component = () => (
3536
hasError={true}
3637
/>
3738
</Spec>
39+
<Spec label="when readonly">
40+
<TimeInput
41+
value={value}
42+
onChange={() => {}}
43+
horizontalConstraint="m"
44+
isReadOnly={true}
45+
/>
46+
</Spec>
47+
<ThemeProvider theme={themes.darkTheme}>
48+
<Spec label="with custom (inverted) theme">
49+
<TimeInput value={value} onChange={() => {}} horizontalConstraint="m" />
50+
</Spec>
51+
</ThemeProvider>
3852
</Suite>
3953
);

0 commit comments

Comments
 (0)