@ndla/ui 15.1.4 → 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 (71) hide show
  1. package/es/Article/ArticleContent.js +2 -0
  2. package/es/Breadcrumb/ActionBreadcrumb.js +9 -4
  3. package/es/Breadcrumb/Breadcrumb.js +6 -5
  4. package/es/FileList/File.js +7 -12
  5. package/es/Filter/FilterButtons.js +13 -13
  6. package/es/MyNdla/Resource/Folder.js +9 -7
  7. package/es/NDLAFilm/CategorySelect.js +51 -25
  8. package/es/NDLAFilm/FilmMovieList.js +14 -10
  9. package/es/NDLAFilm/FilmMovieSearch.js +23 -10
  10. package/es/NDLAFilm/FilmSlideshow.js +117 -19
  11. package/es/NDLAFilm/MovieGrid.js +23 -14
  12. package/es/NDLAFilm/NavigationArrow.js +33 -7
  13. package/es/NDLAFilm/SlideshowIndicator.js +27 -11
  14. package/es/NDLAFilm/filmStyles.js +23 -0
  15. package/es/RadioButtonGroup/RadioButtonGroup.js +28 -9
  16. package/es/Resource/BlockResource.js +7 -7
  17. package/es/Resource/ListResource.js +38 -25
  18. package/es/Resource/resourceComponents.js +17 -18
  19. package/es/all.css +1 -1
  20. package/lib/Article/ArticleContent.js +3 -0
  21. package/lib/Breadcrumb/ActionBreadcrumb.js +9 -4
  22. package/lib/Breadcrumb/Breadcrumb.d.ts +2 -1
  23. package/lib/Breadcrumb/Breadcrumb.js +6 -5
  24. package/lib/FileList/File.js +10 -14
  25. package/lib/Filter/FilterButtons.js +13 -13
  26. package/lib/Frontpage/FrontpageMultidisciplinarySubject.d.ts +5 -5
  27. package/lib/Frontpage/illustrations/FrontpageIllustrations.d.ts +2 -2
  28. package/lib/MultidisciplinarySubject/Illustrations.d.ts +7 -7
  29. package/lib/MyNdla/Resource/Folder.js +8 -6
  30. package/lib/NDLAFilm/CategorySelect.js +52 -30
  31. package/lib/NDLAFilm/FilmMovieList.js +17 -13
  32. package/lib/NDLAFilm/FilmMovieSearch.js +29 -17
  33. package/lib/NDLAFilm/FilmSlideshow.js +113 -24
  34. package/lib/NDLAFilm/MovieGrid.js +25 -15
  35. package/lib/NDLAFilm/NavigationArrow.d.ts +5 -1
  36. package/lib/NDLAFilm/NavigationArrow.js +34 -10
  37. package/lib/NDLAFilm/SlideshowIndicator.js +34 -13
  38. package/lib/NDLAFilm/filmStyles.d.ts +8 -0
  39. package/lib/NDLAFilm/filmStyles.js +38 -0
  40. package/lib/RadioButtonGroup/RadioButtonGroup.js +28 -13
  41. package/lib/Resource/BlockResource.js +6 -6
  42. package/lib/Resource/ListResource.js +34 -23
  43. package/lib/Resource/resourceComponents.d.ts +3 -3
  44. package/lib/Resource/resourceComponents.js +19 -20
  45. package/lib/Search/ContentTypeResultStyles.d.ts +3 -3
  46. package/lib/SearchTypeResult/ActiveFilterContent.d.ts +1 -1
  47. package/lib/all.css +1 -1
  48. package/package.json +11 -11
  49. package/src/Article/ArticleContent.tsx +2 -0
  50. package/src/Breadcrumb/ActionBreadcrumb.tsx +15 -3
  51. package/src/Breadcrumb/Breadcrumb.tsx +10 -2
  52. package/src/FileList/File.tsx +8 -11
  53. package/src/Filter/FilterButtons.tsx +3 -4
  54. package/src/MyNdla/Resource/Folder.tsx +5 -1
  55. package/src/NDLAFilm/CategorySelect.tsx +110 -23
  56. package/src/NDLAFilm/FilmMovieList.tsx +13 -11
  57. package/src/NDLAFilm/FilmMovieSearch.tsx +45 -14
  58. package/src/NDLAFilm/FilmSlideshow.tsx +186 -25
  59. package/src/NDLAFilm/MovieGrid.tsx +33 -25
  60. package/src/NDLAFilm/NavigationArrow.tsx +76 -10
  61. package/src/NDLAFilm/SlideshowIndicator.tsx +53 -11
  62. package/src/NDLAFilm/component.film-movielist.scss +0 -46
  63. package/src/NDLAFilm/filmStyles.ts +33 -0
  64. package/src/RadioButtonGroup/RadioButtonGroup.tsx +82 -11
  65. package/src/Resource/BlockResource.tsx +2 -2
  66. package/src/Resource/ListResource.tsx +56 -31
  67. package/src/Resource/resourceComponents.tsx +8 -9
  68. package/src/main.scss +0 -3
  69. package/src/NDLAFilm/component.film-moviesearch.scss +0 -127
  70. package/src/NDLAFilm/component.film-slideshow.scss +0 -258
  71. package/src/RadioButtonGroup/component.radio-button-group.scss +0 -67
@@ -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
  }
@@ -11,7 +11,7 @@ import React, { ReactNode } from 'react';
11
11
  import SafeLink from '@ndla/safelink';
12
12
  import { colors, fonts, spacing } from '@ndla/core';
13
13
  import Image from '../Image';
14
- import { CompressTagsLength, ResourceImageProps, ResourceTitle, Row, TopicList } from './resourceComponents';
14
+ import { CompressedTags, ResourceImageProps, ResourceTitle, Row, TopicList } from './resourceComponents';
15
15
 
16
16
  interface BlockResourceProps {
17
17
  link: string;
@@ -90,7 +90,7 @@ const BlockResource = ({ link, title, tags, resourceImage, topics, description,
90
90
  <TopicList topics={topics} />
91
91
  <BlockDescription>{description}</BlockDescription>
92
92
  <RightRow>
93
- {tags && CompressTagsLength(tags)}
93
+ {tags && CompressedTags(tags)}
94
94
  {actionMenu}
95
95
  </RightRow>
96
96
  </BlockInfoWrapper>
@@ -9,11 +9,12 @@
9
9
  import styled from '@emotion/styled';
10
10
  import React, { ReactNode } from 'react';
11
11
  import SafeLink from '@ndla/safelink';
12
- import { fonts, spacing, colors } from '@ndla/core';
12
+ import { fonts, spacing, colors, breakpoints, mq } from '@ndla/core';
13
13
  import Image from '../Image';
14
- import { CompressTagsLength, ResourceImageProps, ResourceTitle, Row, TopicList } from './resourceComponents';
14
+ import { CompressedTags, ResourceImageProps, ResourceTitle, TopicList } from './resourceComponents';
15
15
 
16
16
  const ResourceDescription = styled.p`
17
+ grid-area: description;
17
18
  line-clamp: 2;
18
19
  line-height: 1em;
19
20
  height: 3.1em;
@@ -30,14 +31,26 @@ const ResourceDescription = styled.p`
30
31
 
31
32
  const ResourceWrapper = styled(SafeLink)`
32
33
  display: grid;
33
- grid-template-columns: auto 1fr;
34
+ flex: 1;
35
+ grid-template-areas:
36
+ 'image topicAndTitle tags'
37
+ 'image description description';
38
+ grid-template-columns: auto 1fr auto;
39
+ ${mq.range({ until: breakpoints.mobileWide })} {
40
+ grid-template-columns: auto 1fr;
41
+ grid-template-areas:
42
+ 'image topicAndTitle'
43
+ 'description description'
44
+ 'tags tags';
45
+ }
46
+
34
47
  text-decoration: none;
35
48
  box-shadow: none;
36
49
  padding: ${spacing.small};
37
50
  border: 1px solid ${colors.brand.light};
38
51
  border-radius: 2px;
39
52
  color: ${colors.brand.greyDark};
40
- gap: ${spacing.small};
53
+ gap: 0 ${spacing.small};
41
54
  &:hover {
42
55
  box-shadow: 1px 1px 6px 2px rgba(9, 55, 101, 0.08);
43
56
  transition-duration: 0.2s;
@@ -52,27 +65,43 @@ const ResourceWrapper = styled(SafeLink)`
52
65
  }
53
66
  `;
54
67
 
55
- const ResourceInfoWrapper = styled.div`
56
- flex: 1;
68
+ const TagsandActionMenu = styled.div`
69
+ grid-area: tags;
57
70
  display: flex;
58
- flex-direction: column;
59
- max-width: 100%;
71
+ align-items: center;
72
+ width: 100%;
60
73
  overflow: hidden;
74
+ gap: ${spacing.small};
75
+ align-self: flex-start;
76
+ justify-self: flex-end;
77
+ justify-content: flex-end;
61
78
  `;
62
79
 
63
- interface StyledImageProps {
64
- imageSize: 'normal' | 'compact';
65
- }
80
+ const StyledImageWrapper = styled.div<StyledImageProps>`
81
+ grid-area: image;
82
+ width: ${(p) => (p.imageSize === 'normal' ? '136px' : '56px')};
83
+ height: ${(p) => (p.imageSize === 'normal' ? '96px' : '40px')};
84
+ ${mq.range({ until: breakpoints.mobileWide })} {
85
+ width: 54px;
86
+ height: 40px;
87
+ }
88
+ `;
66
89
 
67
- const StyledImage = styled(Image)<StyledImageProps>`
90
+ const StyledImage = styled(Image)`
68
91
  display: flex;
69
92
  border-radius: 2px;
70
93
  object-fit: cover;
71
- width: ${(p) => (p.imageSize === 'normal' ? '136px' : '56px')};
72
- min-width: ${(p) => (p.imageSize === 'normal' ? '136px' : '56px')};
73
- height: ${(p) => (p.imageSize === 'normal' ? '96px' : '40px')};
74
94
  `;
75
95
 
96
+ const TopicAndTitle = styled.div`
97
+ grid-area: topicAndTitle;
98
+ margin-top: ${spacing.xxsmall};
99
+ `;
100
+
101
+ interface StyledImageProps {
102
+ imageSize: 'normal' | 'compact';
103
+ }
104
+
76
105
  export interface ListResourceProps {
77
106
  link: string;
78
107
  title: string;
@@ -88,22 +117,18 @@ const ListResource = ({ link, title, tags, resourceImage, topics, description, a
88
117
 
89
118
  return (
90
119
  <ResourceWrapper to={link}>
91
- <StyledImage alt={resourceImage.alt} src={resourceImage.src} imageSize={showDescription ? 'normal' : 'compact'} />
92
- <ResourceInfoWrapper>
93
- <Row>
94
- <ResourceTitle>{title}</ResourceTitle>
95
- {tags && CompressTagsLength(tags)}
96
- {actionMenu}
97
- </Row>
98
- <Row>
99
- <TopicList topics={topics} />
100
- </Row>
101
- {showDescription && (
102
- <Row>
103
- <ResourceDescription>{description}</ResourceDescription>
104
- </Row>
105
- )}
106
- </ResourceInfoWrapper>
120
+ <StyledImageWrapper imageSize={showDescription ? 'normal' : 'compact'}>
121
+ <StyledImage alt={resourceImage.alt} src={resourceImage.src} />
122
+ </StyledImageWrapper>
123
+ <TopicAndTitle>
124
+ <ResourceTitle>{title}</ResourceTitle>
125
+ <TopicList topics={topics} />
126
+ </TopicAndTitle>
127
+ {showDescription && <ResourceDescription>{description}</ResourceDescription>}
128
+ <TagsandActionMenu>
129
+ {tags && CompressedTags(tags)}
130
+ {actionMenu}
131
+ </TagsandActionMenu>
107
132
  </ResourceWrapper>
108
133
  );
109
134
  };
@@ -31,6 +31,7 @@ export const ResourceTitle = styled.h2`
31
31
  -webkit-line-clamp: 1;
32
32
  line-clamp: 1;
33
33
  -webkit-box-orient: vertical;
34
+ grid-area: resourceTitle;
34
35
  `;
35
36
 
36
37
  const StyledTagList = styled.ul`
@@ -57,6 +58,7 @@ const StyledTopicList = styled.ul`
57
58
  margin: 0;
58
59
  padding: 0;
59
60
  overflow: hidden;
61
+ grid-area: topicList;
60
62
  `;
61
63
 
62
64
  const StyledTopicListElement = styled.li`
@@ -100,9 +102,8 @@ export const TagList = ({ tags }: TagListProps) => {
100
102
  );
101
103
  };
102
104
 
103
- export const CompressTagsLength = (tags: string[]) => {
104
- const tagCounter = tags?.length - 3;
105
- const slicedTags = tags.slice(0, 3);
105
+ export const CompressedTags = (tags: string[]) => {
106
+ const visibleTags = tags.slice(0, 3);
106
107
  const remainingTags = tags.slice(3, tags.length).map((tag) => {
107
108
  return {
108
109
  text: '#' + tag,
@@ -112,10 +113,10 @@ export const CompressTagsLength = (tags: string[]) => {
112
113
 
113
114
  return (
114
115
  <>
115
- <TagList tags={slicedTags} />
116
+ <TagList tags={visibleTags} />
116
117
  {remainingTags.length > 0 && (
117
118
  <MenuButton hideMenuIcon={true} menuItems={remainingTags}>
118
- <TagCounterWrapper>+{tagCounter}</TagCounterWrapper>
119
+ <TagCounterWrapper>{`+${remainingTags.length}`}</TagCounterWrapper>
119
120
  </MenuButton>
120
121
  )}
121
122
  </>
@@ -132,10 +133,8 @@ export const TopicList = ({ topics }: TopicListProps) => {
132
133
  <StyledTopicList>
133
134
  {topics.map((topic, i) => (
134
135
  <StyledTopicListElement key={topic}>
135
- <>
136
- {topic}
137
- {i !== topics.length - 1 && <StyledTopicDivider>•</StyledTopicDivider>}
138
- </>
136
+ {topic}
137
+ {i !== topics.length - 1 && <StyledTopicDivider>•</StyledTopicDivider>}
139
138
  </StyledTopicListElement>
140
139
  ))}
141
140
  </StyledTopicList>
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
- }