@ndla/ui 16.0.0 → 16.1.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.
Files changed (37) hide show
  1. package/es/NDLAFilm/CategorySelect.js +51 -25
  2. package/es/NDLAFilm/FilmMovieList.js +14 -10
  3. package/es/NDLAFilm/FilmMovieSearch.js +23 -10
  4. package/es/NDLAFilm/FilmSlideshow.js +117 -19
  5. package/es/NDLAFilm/MovieGrid.js +23 -14
  6. package/es/NDLAFilm/NavigationArrow.js +33 -7
  7. package/es/NDLAFilm/SlideshowIndicator.js +27 -11
  8. package/es/NDLAFilm/filmStyles.js +23 -0
  9. package/es/RadioButtonGroup/RadioButtonGroup.js +28 -9
  10. package/es/all.css +1 -1
  11. package/lib/NDLAFilm/CategorySelect.js +52 -30
  12. package/lib/NDLAFilm/FilmMovieList.js +17 -13
  13. package/lib/NDLAFilm/FilmMovieSearch.js +29 -17
  14. package/lib/NDLAFilm/FilmSlideshow.js +113 -24
  15. package/lib/NDLAFilm/MovieGrid.js +25 -15
  16. package/lib/NDLAFilm/NavigationArrow.d.ts +5 -1
  17. package/lib/NDLAFilm/NavigationArrow.js +34 -10
  18. package/lib/NDLAFilm/SlideshowIndicator.js +34 -13
  19. package/lib/NDLAFilm/filmStyles.d.ts +8 -0
  20. package/lib/NDLAFilm/filmStyles.js +38 -0
  21. package/lib/RadioButtonGroup/RadioButtonGroup.js +28 -13
  22. package/lib/all.css +1 -1
  23. package/package.json +2 -2
  24. package/src/NDLAFilm/CategorySelect.tsx +110 -23
  25. package/src/NDLAFilm/FilmMovieList.tsx +13 -11
  26. package/src/NDLAFilm/FilmMovieSearch.tsx +45 -14
  27. package/src/NDLAFilm/FilmSlideshow.tsx +186 -25
  28. package/src/NDLAFilm/MovieGrid.tsx +33 -25
  29. package/src/NDLAFilm/NavigationArrow.tsx +76 -10
  30. package/src/NDLAFilm/SlideshowIndicator.tsx +53 -11
  31. package/src/NDLAFilm/component.film-movielist.scss +0 -46
  32. package/src/NDLAFilm/filmStyles.ts +33 -0
  33. package/src/RadioButtonGroup/RadioButtonGroup.tsx +82 -11
  34. package/src/main.scss +0 -3
  35. package/src/NDLAFilm/component.film-moviesearch.scss +0 -127
  36. package/src/NDLAFilm/component.film-slideshow.scss +0 -258
  37. package/src/RadioButtonGroup/component.radio-button-group.scss +0 -67
@@ -7,8 +7,10 @@
7
7
  */
8
8
 
9
9
  import React from 'react';
10
- import BEMHelper from 'react-bem-helper';
11
10
  import { ChevronRight, ChevronLeft } from '@ndla/icons/common';
11
+ import styled from '@emotion/styled';
12
+ import { breakpoints, colors, mq, spacing } from '@ndla/core';
13
+ import { css } from '@emotion/core';
12
14
 
13
15
  interface Props {
14
16
  slideIndexTarget: number;
@@ -17,25 +19,89 @@ interface Props {
17
19
  rightArrow?: boolean;
18
20
  }
19
21
 
20
- const classes = new BEMHelper({
21
- name: 'film-slideshow',
22
- prefix: 'c-',
23
- });
22
+ interface StyledNavigationArrowProps {
23
+ right?: boolean;
24
+ }
25
+
26
+ export const StyledNavigationArrow = styled.div<StyledNavigationArrowProps>`
27
+ opacity: 0;
28
+ transition: transform 800ms ease, opacity 800ms ease;
29
+ display: flex;
30
+ justify-content: center;
31
+ flex-direction: column;
32
+ position: absolute;
33
+ align-items: center;
34
+ z-index: 2;
35
+ height: 70vw;
36
+ ${mq.range({ from: breakpoints.tablet })} {
37
+ height: 60vw;
38
+ }
39
+ ${mq.range({ from: breakpoints.desktop })} {
40
+ height: 55vw;
41
+ }
42
+ ${mq.range({ from: breakpoints.wide })} {
43
+ height: 40vw;
44
+ }
45
+ ${mq.range({ from: breakpoints.ultraWide })} {
46
+ height: 36vw;
47
+ }
48
+ transform: translate(${spacing.xsmall}, 0);
49
+ ${(props) =>
50
+ props.right &&
51
+ `
52
+ right: 0;
53
+ transform: translate(${spacing.xsmall}, 0);
54
+ `}
55
+ `;
56
+
57
+ const NavigationArrowButton = styled.button`
58
+ padding: ${spacing.normal} 0;
59
+ border-radius: 4px;
60
+ outline: none;
61
+ background: transparent;
62
+ color: ${colors.white};
63
+ border: 0;
64
+ &:hover,
65
+ &:focus {
66
+ .c-icon {
67
+ opacity: 1;
68
+ }
69
+ background: rgba(0, 0, 0, 0.1);
70
+ }
71
+ `;
72
+
73
+ const chevronCss = css`
74
+ fill: ${colors.white};
75
+ width: 52px;
76
+ height: 52px;
77
+ ${mq.range({ from: breakpoints.desktop })} {
78
+ width: 78px;
79
+ height: 78px;
80
+ }
81
+ opacity: 0.7;
82
+ transition: transform 400ms ease, opacity 400ms ease;
83
+ ${NavigationArrowButton}:focus {
84
+ opacity: 1;
85
+ }
86
+ ${NavigationArrowButton}:hover {
87
+ opacity: 1;
88
+ }
89
+ `;
24
90
 
25
91
  const NavigationArrow = ({ slideIndexTarget, gotoSlide, rightArrow }: Props) => {
26
92
  const Chevron = rightArrow ? ChevronRight : ChevronLeft;
27
93
 
28
94
  return (
29
- <div {...classes('navigation-arrows', rightArrow ? 'right' : '')}>
30
- <button
95
+ <StyledNavigationArrow right={rightArrow}>
96
+ <NavigationArrowButton
31
97
  type="button"
32
98
  tabIndex={-1}
33
99
  onClick={() => {
34
100
  gotoSlide(slideIndexTarget, true);
35
101
  }}>
36
- <Chevron />
37
- </button>
38
- </div>
102
+ <Chevron css={chevronCss} />
103
+ </NavigationArrowButton>
104
+ </StyledNavigationArrow>
39
105
  );
40
106
  };
41
107
 
@@ -7,7 +7,8 @@
7
7
  */
8
8
 
9
9
  import React from 'react';
10
- import BEMHelper from 'react-bem-helper';
10
+ import styled from '@emotion/styled';
11
+ import { breakpoints, colors, mq, spacing } from '@ndla/core';
11
12
  import { MovieType } from './types';
12
13
 
13
14
  interface Props {
@@ -16,24 +17,65 @@ interface Props {
16
17
  gotoSlide: (indexTarget: number, useAnimation: boolean) => void;
17
18
  }
18
19
 
19
- const classes = new BEMHelper({
20
- name: 'film-slideshow',
21
- prefix: 'c-',
22
- });
20
+ const SlideshowIndicatorWrapper = styled.div`
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ margin: ${spacing.small} 0;
25
+ ${mq.range({ from: breakpoints.mobileWide })} {
26
+ margin: ${spacing.normal} 0;
27
+ }
28
+ `;
29
+
30
+ interface SlideshowIndicatorDotProps {
31
+ active?: boolean;
32
+ }
33
+
34
+ const SlideshowIndicatorDot = styled.button<SlideshowIndicatorDotProps>`
35
+ border: 0;
36
+ display: flex;
37
+ justify-content: center;
38
+ background: transparent;
39
+ span {
40
+ background: ${(props) => (props.active ? colors.white : colors.ndlaFilm.filmColorBright)};
41
+ transition: background 100ms ease;
42
+ height: 8px;
43
+ width: 8px;
44
+ ${mq.range({ from: breakpoints.mobileWide })} {
45
+ height: 10px;
46
+ width: 10px;
47
+ }
48
+ ${mq.range({ from: breakpoints.tablet })} {
49
+ height: ${spacing.small};
50
+ width: ${spacing.small};
51
+ }
52
+ border-radius: 100%;
53
+ }
54
+ padding: ${spacing.xsmall};
55
+ ${mq.range({ from: breakpoints.tablet })} {
56
+ padding: ${spacing.small};
57
+ }
58
+ &:hover,
59
+ &:focus {
60
+ span {
61
+ background: ${colors.white};
62
+ }
63
+ }
64
+ `;
23
65
 
24
66
  const SlideshowIndicator = ({ slideshow, activeSlide, gotoSlide }: Props) => {
25
67
  return (
26
- <div {...classes('indicator-wrapper')}>
27
- {slideshow.map((slide, index) => (
28
- <button
68
+ <SlideshowIndicatorWrapper>
69
+ {slideshow.map((_, index) => (
70
+ <SlideshowIndicatorDot
71
+ active={index === activeSlide}
29
72
  key={`indicator_${index}`}
30
73
  type="button"
31
- {...classes('indicator-dot', index === activeSlide ? 'active' : '')}
32
74
  onClick={() => gotoSlide(index, true)}>
33
75
  <span />
34
- </button>
76
+ </SlideshowIndicatorDot>
35
77
  ))}
36
- </div>
78
+ </SlideshowIndicatorWrapper>
37
79
  );
38
80
  };
39
81
 
@@ -7,52 +7,6 @@
7
7
  }
8
8
 
9
9
  .c-film-movielist {
10
- margin-bottom: $spacing;
11
- @include mq(tablet) {
12
- margin-bottom: $spacing--large;
13
- }
14
- &__heading {
15
- @include font-size(22px, 26px);
16
- font-weight: $font-weight-semibold;
17
- text-transform: uppercase;
18
- letter-spacing: 0.05em;
19
- color: #fff;
20
- margin: $spacing--small 0;
21
- small {
22
- font-weight: normal;
23
- padding-left: $spacing--small;
24
- color: $brand-grey--light;
25
- }
26
- }
27
- &__movie-listing {
28
- display: flex;
29
- flex-wrap: wrap;
30
- margin: $spacing--small 0;
31
- @include set-animations();
32
- > div {
33
- opacity: 0;
34
- animation-fill-mode: forwards;
35
- animation-name: fadeIn;
36
- animation-duration: 300ms;
37
- }
38
- }
39
- &__movie-item {
40
- box-shadow: none;
41
- padding: $spacing--small / 2;
42
- @include mq(tablet) {
43
- margin-bottom: $spacing--large;
44
- }
45
- @include mq($from: tablet) {
46
- width: 25%;
47
- }
48
- @include mq($from: mobileWide, $until: tablet) {
49
- width: 33.3%;
50
- }
51
- @include mq($until: mobileWide) {
52
- width: 50%;
53
- }
54
- }
55
-
56
10
  &__carousel-wrapper-buttons {
57
11
  > div {
58
12
  @include set-animations();
@@ -0,0 +1,33 @@
1
+ import styled from '@emotion/styled';
2
+ import { fonts, spacing, colors } from '@ndla/core';
3
+
4
+ export const setAnimations = () => {
5
+ const styles: any = {};
6
+ for (let i = 1; i < 20; i++) {
7
+ styles[`> div:nth-child(${i + 1})`] = {
8
+ 'animation-delay': `${i * 50}ms`,
9
+ };
10
+ }
11
+ return styles;
12
+ };
13
+
14
+ interface StyledHeadingProps {
15
+ marginLeft?: number;
16
+ }
17
+
18
+ export const StyledHeadingH1 = styled.h1<StyledHeadingProps>`
19
+ ${fonts.sizes('22px', '26px')};
20
+ font-weight: ${fonts.weight.semibold};
21
+ text-transform: uppercase;
22
+ letter-spacing: 0.05em;
23
+ color: ${colors.white};
24
+ margin: ${spacing.small} 0;
25
+ margin-left: ${(props) => props.marginLeft && `${props.marginLeft}px`};
26
+ small {
27
+ font-weight: normal;
28
+ padding-left: ${spacing.small};
29
+ color: ${colors.brand.grey};
30
+ }
31
+ `;
32
+
33
+ export const StyledHeadingH2 = StyledHeadingH1.withComponent('h2');
@@ -7,10 +7,9 @@
7
7
  */
8
8
 
9
9
  import React, { Fragment, Component, ChangeEvent } from 'react';
10
- import BEMHelper from 'react-bem-helper';
10
+ import styled from '@emotion/styled';
11
11
  import { uuid } from '@ndla/util';
12
-
13
- const classes = BEMHelper('c-radio-button-group');
12
+ import { spacing, fonts, colors } from '@ndla/core';
14
13
 
15
14
  interface Props {
16
15
  selected?: string;
@@ -28,6 +27,81 @@ interface State {
28
27
  selected: string;
29
28
  }
30
29
 
30
+ const RadioButtonGroupWrapper = styled.div`
31
+ padding: ${spacing.small} 0;
32
+ font-family: ${fonts.sans};
33
+ display: flex;
34
+ align-items: center;
35
+ `;
36
+
37
+ const RadioButtonGroupLabelHeading = styled.h1`
38
+ ${fonts.sizes('16px', '20px')};
39
+ margin: 0 ${spacing.normal} 0 0;
40
+ font-weight: 600;
41
+ `;
42
+
43
+ const RadioButtonGroupLabel = styled.label`
44
+ ${fonts.sizes('16px', '28px')};
45
+ color: ${colors.brand.primary};
46
+ align-items: center;
47
+ display: inline-flex;
48
+ &:before {
49
+ content: '';
50
+ margin-right: ${spacing.small};
51
+ width: 20px;
52
+ height: 20px;
53
+ border-radius: 100%;
54
+ border: 2px solid ${colors.brand.tertiary};
55
+ transition: 200ms border-color ease;
56
+ }
57
+ &:after {
58
+ content: '';
59
+ background: transparent;
60
+ width: 10px;
61
+ height: 10px;
62
+ border-radius: 100%;
63
+ position: absolute;
64
+ transform: translateX(5px) scale(0, 0);
65
+ transition: 200ms all ease;
66
+ }
67
+ &:not(:last-child) {
68
+ margin-right: ${spacing.medium};
69
+ }
70
+ `;
71
+
72
+ const RadioButtonGroupInput = styled.input`
73
+ opacity: 0;
74
+ position: absolute;
75
+ width: auto;
76
+ &:hover + ${RadioButtonGroupLabel} {
77
+ outline: 1px dotted #212121;
78
+ outline: -webkit-focus-ring-color auto 5px;
79
+ &:after {
80
+ transform: translateX(5px) scale(1, 1);
81
+ background: ${colors.brand.tertiary};
82
+ }
83
+ }
84
+ // emotion does not seem to support several selectors combined with targeting another emotion component
85
+ // so we duplicate the css for :hover and :focus.
86
+ &:focus + ${RadioButtonGroupLabel} {
87
+ outline: 1px dotted #212121;
88
+ outline: -webkit-focus-ring-color auto 5px;
89
+ &:after {
90
+ transform: translateX(5px) scale(1, 1);
91
+ background: ${colors.brand.tertiary};
92
+ }
93
+ }
94
+ &:checked + ${RadioButtonGroupLabel} {
95
+ &:before {
96
+ border-color: ${colors.brand.primary};
97
+ }
98
+ &:after {
99
+ transform: translateX(5px) scale(1, 1);
100
+ background: ${colors.brand.primary};
101
+ }
102
+ }
103
+ `;
104
+
31
105
  class RadioButtonGroup extends Component<Props, State> {
32
106
  private readonly uuid?: string;
33
107
  constructor(props: Props) {
@@ -49,14 +123,13 @@ class RadioButtonGroup extends Component<Props, State> {
49
123
  render() {
50
124
  return (
51
125
  <section>
52
- <div role="radiogroup" {...classes('wrapper')}>
53
- {this.props.label && <h1 {...classes('label-heading')}>{this.props.label}</h1>}
126
+ <RadioButtonGroupWrapper role="radiogroup">
127
+ {this.props.label && <RadioButtonGroupLabelHeading>{this.props.label}</RadioButtonGroupLabelHeading>}
54
128
  {this.props.options.map((option) => {
55
129
  const id = this.uuid ? `${this.uuid}_${option.value}` : option.value;
56
130
  return (
57
131
  <Fragment key={option.value}>
58
- <input
59
- {...classes('input')}
132
+ <RadioButtonGroupInput
60
133
  disabled={option.disabled}
61
134
  aria-checked={this.state.selected === option.value}
62
135
  checked={this.state.selected === option.value}
@@ -66,13 +139,11 @@ class RadioButtonGroup extends Component<Props, State> {
66
139
  name={id}
67
140
  onChange={this.handleOnChange}
68
141
  />
69
- <label htmlFor={id} {...classes('label')}>
70
- {option.title}
71
- </label>
142
+ <RadioButtonGroupLabel htmlFor={id}>{option.title}</RadioButtonGroupLabel>
72
143
  </Fragment>
73
144
  );
74
145
  })}
75
- </div>
146
+ </RadioButtonGroupWrapper>
76
147
  </section>
77
148
  );
78
149
  }
package/src/main.scss CHANGED
@@ -40,7 +40,4 @@
40
40
  @import 'FileList/component.file-list';
41
41
  @import 'SectionHeading/component.section-heading';
42
42
  @import 'AuthorInfo/component.author-info';
43
- @import 'RadioButtonGroup/component.radio-button-group';
44
- @import 'NDLAFilm/component.film-slideshow';
45
43
  @import 'NDLAFilm/component.film-movielist';
46
- @import 'NDLAFilm/component.film-moviesearch';
@@ -1,127 +0,0 @@
1
- .c-film-moviesearch {
2
- margin: $spacing 0 $spacing--large;
3
- &__topic-navigation {
4
- margin: $spacing 0;
5
- @include mq(tablet) {
6
- display: flex;
7
- align-items: flex-start;
8
- padding: 0 $spacing;
9
- }
10
- ul {
11
- list-style-type: none;
12
- list-style-image: none;
13
- display: flex;
14
- align-items: flex-start;
15
- flex-wrap: wrap;
16
- padding: 0;
17
- margin: $spacing--small 0;
18
- @include mq(tablet) {
19
- padding-left: $spacing;
20
- }
21
- li {
22
- padding: 0;
23
- width: 100%;
24
- @include mq(tablet) {
25
- width: 50%;
26
- }
27
- a {
28
- color: #fff;
29
- &:hover,
30
- &:focus {
31
- color: $brand-color--light;
32
- }
33
- }
34
- }
35
- }
36
- }
37
- &__dropdown-button {
38
- border-radius: $border-radius;
39
- @include font-size(22px, 26px);
40
- border: 0;
41
- text-transform: uppercase;
42
- background: $film-color-bright;
43
- color: #fff;
44
- padding: $spacing--small $spacing--small * 1.5;
45
- font-weight: $font-weight-semibold;
46
- display: flex;
47
- align-items: center;
48
- text-align: left;
49
- justify-content: space-between;
50
- width: 100%;
51
- letter-spacing: 0.05em;
52
- @include mq(tablet) {
53
- padding: $spacing--small $spacing;
54
- }
55
- div:first-child {
56
- @include mq($until: tablet) {
57
- display: flex;
58
- flex-direction: column;
59
- small {
60
- padding: 0;
61
- }
62
- }
63
- }
64
- small {
65
- text-transform: none;
66
- padding-left: $spacing--small;
67
- font-weight: normal;
68
- }
69
- }
70
- &__dropdown-container {
71
- position: relative;
72
- margin: 0 0 $spacing;
73
- }
74
- &__dropdown-wrapper {
75
- .c-film-moviesearch__dropdown-button {
76
- border-radius: 0;
77
- text-transform: none;
78
- letter-spacing: 0;
79
- }
80
- display: flex;
81
- flex-direction: column;
82
- padding: $spacing--small 0;
83
- background: #deebf6;
84
- border-radius: $border-radius;
85
- left: 0;
86
- right: 0;
87
- animation: zoomInSelect 200ms ease;
88
- box-shadow: 0 0 30px rgba($black, 0.6);
89
- position: absolute;
90
- z-index: 9999;
91
- button,
92
- a {
93
- color: $primary-color;
94
- border: 0;
95
- outline: 0;
96
- text-align: left;
97
- background: transparent;
98
- padding: $spacing--small;
99
- padding-left: $spacing;
100
- font-weight: $font-weight-semibold;
101
- text-decoration: none;
102
- box-shadow: none;
103
- transition: background 200ms ease;
104
- &:hover,
105
- &:focus {
106
- color: $brand-color;
107
- background: rgba(0, 0, 0, 0.2);
108
- }
109
- }
110
- }
111
- }
112
-
113
- @keyframes zoomInSelect {
114
- 0% {
115
- display: none;
116
- opacity: 0;
117
- }
118
- 1% {
119
- display: flex;
120
- transform: scale3d(0.95, 0.95, 0.95);
121
- opacity: 0;
122
- }
123
- 100% {
124
- opacity: 1;
125
- transform: scale3d(1, 1, 1);
126
- }
127
- }