@woosmap/ui 3.105.0 → 3.108.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@woosmap/ui",
3
- "version": "3.105.0",
3
+ "version": "3.108.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/WebGeoServices/ui.git"
@@ -7,6 +7,9 @@
7
7
  padding 1.6rem
8
8
  border .1rem solid $secondary20
9
9
  cursor pointer
10
+ .view-list &
11
+ flex-direction row
12
+ align-items center
10
13
  &:hover
11
14
  box-shadow 0 0 .6rem $secondary20
12
15
  border .1rem solid $secondary60
@@ -29,9 +32,18 @@
29
32
  &__title
30
33
  font-weight 600
31
34
  margin-bottom .2rem
35
+ .view-list &
36
+ ellipsis()
37
+ order 1
38
+ margin-right 1.2rem
39
+ margin-bottom 0
32
40
  &__subtitle
33
41
  font-size $fontSizeSmall
34
42
  color $secondary60
43
+ .view-list &
44
+ order 3
45
+ margin-left auto
46
+ white-space nowrap
35
47
  &__content
36
48
  margin-top auto
37
49
  padding-top 1rem
@@ -43,6 +55,14 @@
43
55
  right 1rem
44
56
  top 1.3rem
45
57
  fill $favorite
58
+ .view-list &
59
+ sq(1.8)
60
+ position unset
61
+ top unset
62
+ right unset
63
+ order 2
64
+ flex-shrink 0
65
+ margin-right 1.2rem
46
66
  .tooltip
47
67
  position relative
48
68
  $mr = -.2rem
@@ -37,6 +37,12 @@ const units = [
37
37
  { value: 'metric', label: 'Metric' },
38
38
  { value: 'imperial', label: 'Imperial' },
39
39
  ];
40
+
41
+ const methods = [
42
+ { value: 'distance', label: 'Shortest' },
43
+ { value: 'time', label: 'Fastest' },
44
+ ];
45
+
40
46
  export default class DistanceDemo extends Component {
41
47
  constructor(props) {
42
48
  super(props);
@@ -60,6 +66,7 @@ export default class DistanceDemo extends Component {
60
66
  originLocation: this.defaultOrigin.location,
61
67
  destinationLocation: this.defaultDestination.location,
62
68
  unit: units[0],
69
+ method: methods[0],
63
70
  travelMode: travelModes[0],
64
71
  language: (defaultLang || languages[1]).value,
65
72
  alternatives: false,
@@ -161,12 +168,14 @@ export default class DistanceDemo extends Component {
161
168
  })
162
169
  .catch((error) => {
163
170
  const errorResult = error?.data?.error_message ?? 'Unhandled error';
164
- this.setState({ error: errorResult });
171
+ if (this.mounted) {
172
+ this.setState({ error: errorResult });
173
+ }
165
174
  });
166
175
  };
167
176
 
168
177
  getRequestparams = () => {
169
- const { language, originLocation, destinationLocation, unit, travelMode, alternatives } = this.state;
178
+ const { language, originLocation, destinationLocation, unit, method, travelMode, alternatives } = this.state;
170
179
 
171
180
  return {
172
181
  key: Constants.woosmapKey,
@@ -174,6 +183,7 @@ export default class DistanceDemo extends Component {
174
183
  destination: `${destinationLocation.lat},${destinationLocation.lng}`,
175
184
  language,
176
185
  units: unit.value,
186
+ method: method.value,
177
187
  mode: travelMode.value,
178
188
  alternatives,
179
189
  };
@@ -264,7 +274,7 @@ export default class DistanceDemo extends Component {
264
274
  };
265
275
 
266
276
  renderFooterFilters = () => {
267
- const { unit, travelMode, language, alternatives } = this.state;
277
+ const { unit, method, travelMode, language, alternatives } = this.state;
268
278
 
269
279
  const filterTravelMode = {
270
280
  key: 'travelmode',
@@ -304,7 +314,26 @@ export default class DistanceDemo extends Component {
304
314
  ),
305
315
  };
306
316
 
317
+ const filterMethod = {
318
+ key: 'method',
319
+ component: (
320
+ <ButtonGroup isLight>
321
+ {methods.map((item) => (
322
+ <Button
323
+ type="group"
324
+ size="small"
325
+ key={item.value}
326
+ label={item.label}
327
+ active={item === method}
328
+ onClick={() => this.setState({ method: item }, this.requestDistance)}
329
+ />
330
+ ))}
331
+ </ButtonGroup>
332
+ ),
333
+ };
334
+
307
335
  const filterLanguage = {
336
+ key: 'language',
308
337
  label: tr('Language'),
309
338
  component: (
310
339
  <ButtonGroup className="language" isLight>
@@ -326,6 +355,7 @@ export default class DistanceDemo extends Component {
326
355
  };
327
356
 
328
357
  const filterAlternatives = {
358
+ key: 'alternatives',
329
359
  label: '',
330
360
  component: (
331
361
  <Input
@@ -339,7 +369,7 @@ export default class DistanceDemo extends Component {
339
369
  ),
340
370
  };
341
371
 
342
- return [filterAlternatives, filterTravelMode, filterUnit, filterLanguage];
372
+ return [filterAlternatives, filterTravelMode, filterMethod, filterLanguage, filterUnit];
343
373
  };
344
374
 
345
375
  render() {
@@ -23,6 +23,13 @@ it('test metrics button', () => {
23
23
  expect(screen.getByText('units=imperial&\\')).toBeVisible();
24
24
  });
25
25
 
26
+ it('test method button', () => {
27
+ render(<DistanceDemo />);
28
+ expect(screen.getByText('method=distance&\\')).toBeVisible();
29
+ fireEvent.click(screen.getByLabelText('Fastest'));
30
+ expect(screen.getByText('method=time&\\')).toBeVisible();
31
+ });
32
+
26
33
  it('test language button', () => {
27
34
  render(<DistanceDemo />);
28
35
  expect(screen.getByText('language=gb&\\')).toBeVisible();
@@ -33,11 +40,11 @@ it('test language button', () => {
33
40
  it('test alternative route checkbox', () => {
34
41
  render(<DistanceDemo />);
35
42
  expect(screen.getByText('alternatives=false"')).toBeVisible();
36
- // eslint-disable-next-line prefer-destructuring
37
- const checkbox = screen.getByText('Alternative routes').parentElement.querySelectorAll('input')[0];
43
+ const [checkbox] = screen.getByText('Alternative routes').parentElement.querySelectorAll('input');
38
44
  fireEvent.click(checkbox);
39
45
  expect(screen.getByText('alternatives=true"')).toBeVisible();
40
46
  });
47
+
41
48
  it('catches 200 with error', async () => {
42
49
  const backup = axios.create;
43
50
  axios.create = () => ({
@@ -147,16 +147,12 @@ export default class SkeletonDemo extends Component {
147
147
  if (!footerFilters || footerFilters.length === 0) {
148
148
  return null;
149
149
  }
150
- return (
151
- <>
152
- {footerFilters.map((filter) => (
153
- <div className="demo__input-container" key={filter.label || filter.key}>
154
- {filter.label ? <p className="demo__label demo__label--filter">{filter.label}</p> : false}
155
- {filter.component}
156
- </div>
157
- ))}
158
- </>
159
- );
150
+ return footerFilters.map((filter) => (
151
+ <div className="demo__input-container" key={filter.label || filter.key}>
152
+ {filter.label ? <p className="demo__label demo__label--filter">{filter.label}</p> : false}
153
+ {filter.component}
154
+ </div>
155
+ ));
160
156
  };
161
157
 
162
158
  render() {
@@ -8,7 +8,7 @@ import withClickOutside from '../withClickOutside/withClickOutside';
8
8
  class Panel extends Component {
9
9
  constructor(props) {
10
10
  super(props);
11
- this.state = { open: false };
11
+ this.state = { open: false, fakeValues: [] };
12
12
  this.clickOutsideRef = React.createRef();
13
13
  }
14
14
 
@@ -23,7 +23,25 @@ class Panel extends Component {
23
23
  this.close();
24
24
  };
25
25
 
26
+ setFakeValues = () => {
27
+ const { numberOfPlaceholderSections } = this.props;
28
+ this.setState({
29
+ fakeValues: Array(numberOfPlaceholderSections)
30
+ .fill(null)
31
+ .map((_, containerIndex) =>
32
+ Array(2)
33
+ .fill(null)
34
+ .map((__, lineIndex) => ({
35
+ width: Math.floor(Math.random() * (10 - 90) + 90),
36
+ containerIndex,
37
+ lineIndex,
38
+ }))
39
+ ),
40
+ });
41
+ };
42
+
26
43
  open = () => {
44
+ this.setFakeValues();
27
45
  this.setState({ open: true });
28
46
  };
29
47
 
@@ -48,9 +66,30 @@ class Panel extends Component {
48
66
  }
49
67
  };
50
68
 
69
+ getPlaceholderSections = () => {
70
+ const { fakeValues } = this.state;
71
+ return (
72
+ <div className="panel__body__fake-content">
73
+ {fakeValues.map((section) => (
74
+ <div className="panel__body__fake-content__section" key={`section-${section[0].containerIndex}`}>
75
+ {section.map(({ width, containerIndex, lineIndex }) => (
76
+ <div
77
+ className="panel__fake-line"
78
+ key={`section-${containerIndex}-${lineIndex}`}
79
+ style={{ width: `${width}%` }}
80
+ />
81
+ ))}
82
+ </div>
83
+ ))}
84
+ </div>
85
+ );
86
+ };
87
+
88
+ getPlaceholderHeader = () => <span className="panel__header__fake-action" />;
89
+
51
90
  render() {
52
91
  const { open } = this.state;
53
- const { children, position, header, disableCloseOutside, testId, ...rest } = this.props;
92
+ const { position, header, disableCloseOutside, loading, testId, children, ...rest } = this.props;
54
93
  return (
55
94
  <AnimatePresence>
56
95
  {open && (
@@ -69,7 +108,10 @@ class Panel extends Component {
69
108
  {...rest}
70
109
  >
71
110
  <div className="panel__header">
72
- {header && <div className="panel__header__content">{header}</div>}
111
+ {loading
112
+ ? this.getPlaceholderHeader()
113
+ : header && <div className="panel__header__content">{header}</div>}
114
+
73
115
  <div className="panel__header__action">
74
116
  <Button
75
117
  className={cl('panel__header__action__btn')}
@@ -80,7 +122,7 @@ class Panel extends Component {
80
122
  />
81
123
  </div>
82
124
  </div>
83
- <div className="panel__body">{children}</div>
125
+ <div className="panel__body">{loading ? this.getPlaceholderSections() : children}</div>
84
126
  </motion.div>
85
127
  )}
86
128
  </AnimatePresence>
@@ -95,6 +137,8 @@ Panel.defaultProps = {
95
137
  header: null,
96
138
  disableCloseOutside: false,
97
139
  testId: 'panel',
140
+ loading: false,
141
+ numberOfPlaceholderSections: 6,
98
142
  };
99
143
 
100
144
  Panel.propTypes = {
@@ -105,6 +149,8 @@ Panel.propTypes = {
105
149
  children: PropTypes.node.isRequired,
106
150
  position: PropTypes.oneOf(['right', 'left']),
107
151
  header: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
152
+ loading: PropTypes.bool,
153
+ numberOfPlaceholderSections: PropTypes.number,
108
154
  };
109
155
 
110
156
  export default withClickOutside(Panel, '.ignore-click-outside-panel');
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useRef, useState } from 'react';
2
2
  import Panel from './Panel';
3
3
  import Button from '../Button/Button';
4
4
 
@@ -10,11 +10,45 @@ const Story = {
10
10
  export default Story;
11
11
 
12
12
  const Template = () => {
13
- const panelRef = React.createRef();
13
+ const [loading, setLoading] = useState(false);
14
+ const panelRef = useRef(null);
15
+ const loadingPanelRef = useRef(null);
16
+
17
+ const openLoadingPanel = () => {
18
+ setLoading(true);
19
+ loadingPanelRef.current.toggle();
20
+ setTimeout(() => setLoading(false), 3000);
21
+ };
22
+
23
+ const getHeader = () => <Button icon="settings" />;
24
+
14
25
  return (
15
26
  <div>
16
- <Button onClick={() => panelRef.current.toggle()} label="Toggle panel" />
27
+ <div>
28
+ <Button onClick={() => panelRef.current.toggle()} label="Toggle panel" />
29
+ </div>
30
+ <div>
31
+ <Button onClick={openLoadingPanel} label="Toggle loading panel" />
32
+ </div>
17
33
  <Panel ref={panelRef}>My panel</Panel>
34
+ <Panel ref={loadingPanelRef} loading={loading} header={getHeader()}>
35
+ <div>
36
+ <div>some info 1</div>
37
+ <div>some sub info 1</div>
38
+ </div>
39
+ <div>
40
+ <div>some info 2</div>
41
+ <div>some sub info 2</div>
42
+ </div>
43
+ <div>
44
+ <div>some info 3</div>
45
+ <div>some sub info 3</div>
46
+ </div>
47
+ <div>
48
+ <div>some info 3</div>
49
+ <div>some sub info 3</div>
50
+ </div>
51
+ </Panel>
18
52
  </div>
19
53
  );
20
54
  };
@@ -1,3 +1,8 @@
1
+ @keyframes gradient
2
+ 0%
3
+ background-position 80% 0%
4
+ 100%
5
+ background-position -20% 0%
1
6
  .panel
2
7
  position absolute
3
8
  z-index 10
@@ -44,6 +49,13 @@
44
49
  fullw()
45
50
  display flex
46
51
  align-items center
52
+ &__fake-action
53
+ sq()
54
+ br(50)
55
+ background $dark20
56
+ background linear-gradient(90deg, $dark20 20%, $dark30 30%, $dark20 40%)
57
+ background-size 400% 400%
58
+ animation gradient 1.4s ease-in-out infinite
47
59
  &__body
48
60
  fullh()
49
61
  mbib(4)
@@ -88,6 +100,37 @@
88
100
  top - .2rem
89
101
  content "\2022"
90
102
  color $secondary-medium60
103
+ &__fake-content
104
+ padding 2rem
105
+ width 100%
106
+ &__section
107
+ mbib(1)
108
+ width 100%
109
+ margin-bottom 4rem
110
+ &:first-child
111
+ .panel__fake-line
112
+ &:first-child
113
+ height 2rem
114
+ background $dark20
115
+ background linear-gradient(90deg, $dark20 20%, $dark30 30%, $dark20 40%)
116
+ background-size 400% 400%
117
+ animation gradient 1.4s ease-in-out infinite
118
+ &:last-child
119
+ height 1rem
120
+ &__fake-line
121
+ br(10)
122
+ background $dark6
123
+ background linear-gradient(90deg, $dark6 20%, $dark10 30%, $dark6 40%)
124
+ background-size 400% 400%
125
+ animation gradient 1.4s ease-in-out infinite
126
+ height 1rem
127
+ &:nth-child(even)
128
+ background $dark6
129
+ background linear-gradient(90deg, $dark10 20%, $dark20 30%, $dark10 40%)
130
+ background-size 400% 400%
131
+ animation gradient 1.4s ease-in-out infinite
132
+ height 1.2rem
133
+
91
134
  @media screen and (max-width 720px)
92
135
  .panel
93
136
  width 100%
@@ -35,3 +35,19 @@ it('is hidden after clicking close', () => {
35
35
  // nothink
36
36
  }
37
37
  });
38
+
39
+ it('opens on placeholder if loading prop is passed down and with provided num of placeholder sections', () => {
40
+ const panelRef = React.createRef();
41
+ const { container } = render(
42
+ <Panel ref={panelRef} loading numberOfPlaceholderSections={4}>
43
+ content
44
+ </Panel>
45
+ );
46
+ act(() => {
47
+ panelRef.current.open();
48
+ });
49
+ expect(screen.queryByText('content')).not.toBeInTheDocument();
50
+ expect(container.querySelector('.panel__body__fake-content')).toBeInTheDocument();
51
+ expect(container.querySelectorAll('.panel__body__fake-content__section').length).toBe(4);
52
+ expect(container.querySelectorAll('.panel__fake-line').length).toBe(8);
53
+ });