@instructure/ui-pill 8.48.3 → 8.48.4-snapshot-0

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.
@@ -0,0 +1,86 @@
1
+ /*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2015 - present Instructure, Inc.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ import React from 'react'
26
+ import { render } from '@testing-library/react'
27
+ import '@testing-library/jest-dom'
28
+
29
+ // eslint-disable-next-line no-restricted-imports
30
+ import { runAxeCheck } from '@instructure/ui-axe-check'
31
+ import { Pill } from '../index'
32
+ import { IconEyeLine } from '@instructure/ui-icons'
33
+
34
+ const originalResizeObserver = global.ResizeObserver
35
+
36
+ describe('<Pill />', () => {
37
+ beforeAll(() => {
38
+ // Mock for ResizeObserver browser API
39
+ global.ResizeObserver = jest.fn().mockImplementation(() => ({
40
+ observe: jest.fn(),
41
+ unobserve: jest.fn(),
42
+ disconnect: jest.fn()
43
+ }))
44
+ })
45
+
46
+ it('should render', async () => {
47
+ const { container } = render(<Pill>Overdue</Pill>)
48
+
49
+ expect(container).toHaveTextContent('Overdue')
50
+ })
51
+
52
+ it('should display status text', async () => {
53
+ const { container } = render(<Pill statusLabel="Statuslabel">Overdue</Pill>)
54
+
55
+ expect(container).toHaveTextContent('Statuslabel:')
56
+ expect(container).toHaveTextContent('Overdue')
57
+ })
58
+
59
+ it('should render icon text', async () => {
60
+ const { container } = render(
61
+ <Pill
62
+ statusLabel="Statuslabel"
63
+ renderIcon={<IconEyeLine color="auto" title="Love" />}
64
+ >
65
+ Overdue
66
+ </Pill>
67
+ )
68
+
69
+ const svg = container.querySelector('svg')
70
+
71
+ expect(container).toHaveTextContent('Statuslabel:')
72
+ expect(container).toHaveTextContent('Overdue')
73
+ expect(svg).toHaveAttribute('name', 'IconEye')
74
+ })
75
+
76
+ it('should be accessible', async () => {
77
+ const { container } = render(<Pill>Overdue</Pill>)
78
+ const axeCheck = await runAxeCheck(container)
79
+
80
+ expect(axeCheck).toBe(true)
81
+ })
82
+
83
+ afterAll(() => {
84
+ global.ResizeObserver = originalResizeObserver
85
+ })
86
+ })
@@ -26,7 +26,6 @@
26
26
  import { Component } from 'react'
27
27
 
28
28
  import { View } from '@instructure/ui-view'
29
- import { TruncateText } from '@instructure/ui-truncate-text'
30
29
  import { passthroughProps } from '@instructure/ui-react-utils'
31
30
  import { testable } from '@instructure/ui-testable'
32
31
  import { Tooltip } from '@instructure/ui-tooltip'
@@ -59,6 +58,8 @@ class Pill extends Component<PillProps, PillState> {
59
58
 
60
59
  ref: Element | null = null
61
60
 
61
+ ellipsisRef: HTMLElement | null = null
62
+
62
63
  constructor(props: PillProps) {
63
64
  super(props)
64
65
 
@@ -68,6 +69,7 @@ class Pill extends Component<PillProps, PillState> {
68
69
  }
69
70
 
70
71
  componentDidMount() {
72
+ this.setTruncation()
71
73
  this.props.makeStyles?.()
72
74
  }
73
75
 
@@ -75,10 +77,10 @@ class Pill extends Component<PillProps, PillState> {
75
77
  this.props.makeStyles?.()
76
78
  }
77
79
 
78
- handleTruncation(truncated: boolean) {
79
- if (truncated !== this.state.truncated) {
80
+ setTruncation() {
81
+ if (this.ellipsisRef) {
80
82
  this.setState({
81
- truncated: truncated
83
+ truncated: this.ellipsisRef.offsetWidth < this.ellipsisRef.scrollWidth
82
84
  })
83
85
  }
84
86
  }
@@ -105,6 +107,8 @@ class Pill extends Component<PillProps, PillState> {
105
107
  elementRef,
106
108
  styles,
107
109
  makeStyles,
110
+ statusLabel,
111
+ renderIcon,
108
112
  ...props
109
113
  } = this.props
110
114
 
@@ -132,17 +136,22 @@ class Pill extends Component<PillProps, PillState> {
132
136
  withFocusOutline={focused}
133
137
  focusColor="info"
134
138
  >
135
- <span css={styles?.pill}>
136
- <span css={styles?.text}>
137
- <TruncateText
138
- onUpdate={(truncated) => {
139
- this.handleTruncation(truncated)
140
- }}
141
- >
142
- {children}
143
- </TruncateText>
144
- </span>
145
- </span>
139
+ <div css={styles?.pill}>
140
+ {renderIcon && <div css={styles?.icon}>{renderIcon}</div>}
141
+ <div
142
+ css={styles?.text}
143
+ ref={(el) => {
144
+ this.ellipsisRef = el
145
+ }}
146
+ >
147
+ {statusLabel && (
148
+ <span css={styles?.status}>
149
+ {statusLabel && statusLabel.concat(':')}
150
+ </span>
151
+ )}
152
+ {children}
153
+ </div>
154
+ </div>
146
155
  </View>
147
156
  )
148
157
  }
@@ -150,7 +159,17 @@ class Pill extends Component<PillProps, PillState> {
150
159
  render() {
151
160
  if (this.state.truncated) {
152
161
  return (
153
- <Tooltip renderTip={this.props.children} elementRef={this.handleRef}>
162
+ <Tooltip
163
+ renderTip={
164
+ this.props.statusLabel
165
+ ? this.props.statusLabel.concat(
166
+ ': ',
167
+ this.props.children as string
168
+ )
169
+ : this.props.children
170
+ }
171
+ elementRef={this.handleRef}
172
+ >
154
173
  {({ focused, getTriggerProps }) => {
155
174
  return this.renderPill(focused, getTriggerProps)
156
175
  }}
package/src/Pill/props.ts CHANGED
@@ -52,6 +52,16 @@ type PillOwnProps = {
52
52
  */
53
53
  margin?: Spacing
54
54
  children: React.ReactNode
55
+
56
+ /**
57
+ * Adds a status label to the left of the main text.
58
+ */
59
+ statusLabel?: string
60
+
61
+ /**
62
+ * An icon displayed to the left of the text.
63
+ */
64
+ renderIcon?: React.ReactNode
55
65
  }
56
66
  type PropKeys = keyof PillOwnProps
57
67
 
@@ -61,7 +71,9 @@ type PillProps = PillOwnProps &
61
71
  WithStyleProps<PillTheme, PillStyle> &
62
72
  OtherHTMLAttributes<PillOwnProps>
63
73
 
64
- type PillStyle = ComponentStyle<'pill' | 'text' | 'maxWidth'>
74
+ type PillStyle = ComponentStyle<
75
+ 'pill' | 'text' | 'maxWidth' | 'status' | 'icon'
76
+ >
65
77
 
66
78
  const propTypes: PropValidators<PropKeys> = {
67
79
  as: PropTypes.elementType, // eslint-disable-line react/require-default-props
@@ -75,7 +87,9 @@ const propTypes: PropValidators<PropKeys> = {
75
87
  'alert'
76
88
  ]),
77
89
  elementRef: PropTypes.func,
78
- margin: ThemeablePropTypes.spacing
90
+ margin: ThemeablePropTypes.spacing,
91
+ statusLabel: PropTypes.string,
92
+ renderIcon: PropTypes.oneOfType([PropTypes.node, PropTypes.func])
79
93
  }
80
94
 
81
95
  const allowedProps: AllowedPropKeys = [
@@ -71,7 +71,9 @@ const generateStyle = (
71
71
  return {
72
72
  pill: {
73
73
  label: 'pill',
74
- display: 'block',
74
+ display: 'flex',
75
+ alignItems: 'center',
76
+ justifyContent: 'center',
75
77
  fontFamily: componentTheme.fontFamily,
76
78
  boxSizing: 'border-box',
77
79
  padding: componentTheme.padding,
@@ -83,13 +85,29 @@ const generateStyle = (
83
85
  lineHeight: `calc(${componentTheme.height} - (${componentTheme.borderWidth} * 2))`,
84
86
  ...pillColorVariants[color!]
85
87
  },
88
+ status: {
89
+ label: 'pill__status',
90
+ boxSizing: 'border-box',
91
+ fontSize: componentTheme.textFontSize,
92
+ fontWeight: componentTheme.statusLabelFontWeight,
93
+ marginRight: '0.125rem'
94
+ },
95
+ icon: {
96
+ label: 'pill__icon',
97
+ display: 'flex',
98
+ alignItems: 'center',
99
+ marginRight: '0.375rem',
100
+ fontSize: '0.75rem'
101
+ },
86
102
  text: {
87
103
  label: 'pill__text',
88
104
  boxSizing: 'border-box',
89
- textTransform: componentTheme.textTransformStyle,
105
+ maxWidth: componentTheme.maxWidth,
90
106
  fontSize: componentTheme.textFontSize,
91
107
  fontWeight: componentTheme.textFontWeight,
92
- letterSpacing: '0.0625rem'
108
+ textOverflow: 'ellipsis',
109
+ whiteSpace: 'nowrap',
110
+ overflow: 'hidden'
93
111
  },
94
112
  maxWidth: componentTheme.maxWidth
95
113
  }
package/src/Pill/theme.ts CHANGED
@@ -42,11 +42,11 @@ const generateComponentTheme = (theme: Theme): PillTheme => {
42
42
  const componentVariables: PillTheme = {
43
43
  fontFamily: typography?.fontFamily,
44
44
  padding: `0 ${spacing?.xSmall}`,
45
- height: '1.3125rem',
45
+ height: '1.5rem',
46
46
  background: colors?.backgroundLightest,
47
- textTransformStyle: 'uppercase',
48
- textFontSize: typography?.fontSizeXSmall,
49
- textFontWeight: typography?.fontWeightBold,
47
+ textFontSize: typography?.fontSizeSmall,
48
+ textFontWeight: typography?.fontWeightNormal,
49
+ statusLabelFontWeight: typography?.fontWeightBold,
50
50
  maxWidth: '15rem',
51
51
  primaryColor: colors?.textDark,
52
52
  infoColor: colors?.textInfo,
@@ -21,6 +21,9 @@
21
21
  { "path": "../ui-test-locator/tsconfig.build.json" },
22
22
  { "path": "../ui-test-queries/tsconfig.build.json" },
23
23
  { "path": "../ui-test-utils/tsconfig.build.json" },
24
+ { "path": "../ui-axe-check/tsconfig.build.json" },
25
+ { "path": "../ui-scripts/tsconfig.build.json" },
26
+ { "path": "../ui-icons/tsconfig.build.json" },
24
27
  { "path": "../ui-themes/tsconfig.build.json" }
25
28
  ]
26
29
  }