@jetshop/template-trend 5.11.2 → 5.12.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.
@@ -1,3 +1,5 @@
1
- .Header_h1j1g21v{font-size:1.6875rem;font-weight:600;margin:1rem 0 3rem;}
2
- .Content_c1fnqd8r{padding-bottom:1rem;}.Content_c1fnqd8r p{margin-bottom:1rem;}
3
- .MaxWidth_m19kzxi7.MaxWidth_mjqp9b6{max-width:60rem;}
1
+ .Content_c1j1g21v{padding-bottom:1rem;}.Content_c1j1g21v p{margin-bottom:1rem;}
2
+ .container_c1fnqd8r{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;}
3
+ .sidebar_s19kzxi7{max-width:288px;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;border-right:1px solid #dedede;margin-right:2rem;padding:2rem 0;}@media (max-width:799px){.sidebar_s19kzxi7{display:none;}}
4
+ .content_c18mz79g{max-width:60rem;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;padding:2rem 0;}.content_c18mz79g h1{margin-bottom:1rem;}
5
+ .body_bqf7s3s{margin-top:1rem;}
@@ -0,0 +1,4 @@
1
+ .menu_mn7zz18{width:100%;}@media (min-width:50em){.menu_mn7zz18{display:none;}}.menu_mn7zz18 [data-flight-dropdown-button]{width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0.75em;border:1px solid #dedede;width:100%;text-align:left;background:#FFFFFF;border-radius:3px;outline:none;}.menu_mn7zz18 [data-flight-dropdown-button]:focus{color:#146DE1;}.menu_mn7zz18 [data-flight-dropdown-button][aria-expanded='true']{border-radius:3px 3px 0 0;border-bottom:0;color:#146DE1;}.menu_mn7zz18 [data-flight-dropdown-button][aria-expanded='true'] .carot{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg);}.menu_mn7zz18 [data-flight-dropdown-items]{position:absolute;top:100%;left:0;background:white;display:block;z-index:2;outline:none;width:100%;border:1px solid #dedede;border-top:0;}.menu_mn7zz18 [data-flight-dropdown-item]{line-height:1.5;border:none;cursor:pointer;white-space:normal;padding:0.5em 1em;}.menu_mn7zz18 [data-flight-dropdown-item]:hover{color:blue;}.menu_mn7zz18 [data-flight-dropdown-item] a{-webkit-text-decoration:none;text-decoration:none;color:#000;}.menu_mn7zz18 [data-flight-dropdown-item]:focus{font-weight:bold;background:#f2f2f2;outline:none;position:relative;}.menu_mn7zz18 [data-flight-dropdown-item]:focus:before{content:'';position:absolute;top:0;bottom:0;left:0;width:2px;background-color:#2f80ed;}
2
+ .links_l4wjvty{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}
3
+ .link_lkx9gc5{color:#000;-webkit-text-decoration:none;text-decoration:none;margin-bottom:1rem;line-height:1.5;}
4
+ .activeLink_a1iunu17{font-weight:bold;color:#2f80ed;}
@@ -1,2 +1,2 @@
1
- .StyledBreadcrumbs_s11md4e4{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;list-style:none;margin-bottom:2rem;}.StyledBreadcrumbs_s11md4e4 a,.StyledBreadcrumbs_s11md4e4 li{color:#707070;font-size:0.75rem;}.StyledBreadcrumbs_s11md4e4.hide-last li:last-child:after{display:none;}
1
+ .StyledBreadcrumbs_s11md4e4{line-height:1.5;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;list-style:none;margin-bottom:2rem;}.StyledBreadcrumbs_s11md4e4 a,.StyledBreadcrumbs_s11md4e4 li{color:#707070;font-size:0.75rem;}.StyledBreadcrumbs_s11md4e4.hide-last li:last-child:after{display:none;}
2
2
  .Breadcrumb_b17ekgas{white-space:nowrap;}.Breadcrumb_b17ekgas:after{display:inline-block;content:'/';padding:0 0.33em;}
@@ -1,2 +1,2 @@
1
1
  .DropdownMenu0_dv23fto global([data-flight-dropdown-open='false'] [data-flight-dropdown-items]){display:none;}
2
- .styles_ssx52uf{position:relative;}.styles_ssx52uf [data-flight-dropdown-button]{padding:0.5em 1em;border:1px solid #c0c0c0;border-radius:3px;}.styles_ssx52uf [data-flight-dropdown-items]{position:absolute;position:absolute;top:100%;left:0;background:white;white-space:nowrap;display:block;z-index:2;outline:none;}.styles_ssx52uf [data-flight-dropdown-item]{cursor:pointer;border-top:1px solid #c0c0c0;white-space:nowrap;padding:0.5em 1em;}.styles_ssx52uf [data-flight-dropdown-item]:hover{color:blue;}
2
+ .styles_ssx52uf{position:relative;}.styles_ssx52uf [data-flight-dropdown-button]{padding:0.5em 1em;border:1px solid #c0c0c0;border-radius:3px;}.styles_ssx52uf [data-flight-dropdown-items]{position:absolute;top:100%;left:0;background:white;white-space:nowrap;display:block;z-index:2;outline:none;}.styles_ssx52uf [data-flight-dropdown-item]{cursor:pointer;border-top:1px solid #c0c0c0;white-space:nowrap;padding:0.5em 1em;}.styles_ssx52uf [data-flight-dropdown-item]:hover{color:blue;}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetshop/template-trend",
3
- "version": "5.11.2",
3
+ "version": "5.12.0",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "build": "react-scripts build",
@@ -45,11 +45,11 @@
45
45
  ]
46
46
  },
47
47
  "dependencies": {
48
- "@jetshop/core": "^5.11.2",
48
+ "@jetshop/core": "^5.12.0",
49
49
  "@jetshop/flight-shortcodes": "^2.0.10",
50
- "@jetshop/intl": "^5.11.2",
51
- "@jetshop/react-scripts": "^5.11.2",
52
- "@jetshop/ui": "^5.11.2",
50
+ "@jetshop/intl": "^5.12.0",
51
+ "@jetshop/react-scripts": "^5.12.0",
52
+ "@jetshop/ui": "^5.12.0",
53
53
  "@react-google-maps/api": "~1.7.0",
54
54
  "prop-types": "^15.6.2",
55
55
  "react": "^16.9.0",
package/schema.graphql CHANGED
@@ -212,7 +212,10 @@ type Category implements Document {
212
212
  """
213
213
  isDynamic: Boolean!
214
214
 
215
- """Get content data set via the Content Editor"""
215
+ """
216
+ Get content data set via the Content Editor.NB: Carries a performance cost, as
217
+ asking for this will result in a separate API call in the backend.
218
+ """
216
219
  data: Content
217
220
  }
218
221
 
@@ -1356,7 +1359,7 @@ type PagedResult {
1356
1359
  result: [Product]!
1357
1360
  }
1358
1361
 
1359
- """The `Paging` scalar type represents a numeric values between (0; 100>"""
1362
+ """The `Paging` scalar type represents a numeric values between <1; 100>"""
1360
1363
  scalar Paging
1361
1364
 
1362
1365
  input PagingInput {
@@ -164,8 +164,16 @@ const CartFlyoutView = ({ result, modal, ...rest }) => {
164
164
  <Checkout
165
165
  data-testid="checkout-button"
166
166
  href={checkoutUrl}
167
- onClick={() => {
168
- track(trackCartCheckoutEvent({ cart: result.data.cart }));
167
+ onClick={event => {
168
+ event.preventDefault();
169
+ track(
170
+ trackCartCheckoutEvent({
171
+ cart: result.data.cart,
172
+ callback: () => {
173
+ window.location = checkoutUrl;
174
+ }
175
+ })
176
+ );
169
177
  }}
170
178
  >
171
179
  {t('Check out')}
@@ -74,7 +74,7 @@ export function ProductCard({
74
74
  }) {
75
75
  const hasImages = product.images && product.images.length > 0;
76
76
  const Tag = as;
77
- let badges = [...product.badges];
77
+ let badges = [...(product.badges || [])];
78
78
  product.isPackage &&
79
79
  badges.push({
80
80
  location: 'TOP_RIGHT',
@@ -3,12 +3,13 @@ import { styled } from 'linaria/react';
3
3
  import React from 'react';
4
4
  import UIMaxWidth from '../Layout/MaxWidth';
5
5
  import { ContentRenderer } from '@jetshop/ui/ContentRenderer';
6
-
7
- const Header = styled('h1')`
8
- font-size: 1.6875rem;
9
- font-weight: 600;
10
- margin: 1rem 0 3rem;
11
- `;
6
+ import {
7
+ MobileNavigation,
8
+ DesktopNavigation,
9
+ shouldDisplayNavigation
10
+ } from './SubPageNavigation';
11
+ import { css } from 'linaria';
12
+ import Breadcrumbs from '@jetshop/ui/Breadcrumbs';
12
13
 
13
14
  const Content = styled('div')`
14
15
  padding-bottom: 1rem;
@@ -18,8 +19,35 @@ const Content = styled('div')`
18
19
  }
19
20
  `;
20
21
 
21
- const MaxWidth = styled(UIMaxWidth)`
22
+ const container = css`
23
+ display: flex;
24
+ flex-direction: row;
25
+ `;
26
+
27
+ const sidebar = css`
28
+ max-width: 288px;
29
+ flex: 1 1 auto;
30
+ border-right: 1px solid #dedede;
31
+ margin-right: 2rem;
32
+ padding: 2rem 0;
33
+
34
+ @media (max-width: 799px) {
35
+ display: none;
36
+ }
37
+ `;
38
+
39
+ const content = css`
22
40
  max-width: 60rem;
41
+ flex: 1 1 auto;
42
+ padding: 2rem 0;
43
+
44
+ h1 {
45
+ margin-bottom: 1rem;
46
+ }
47
+ `;
48
+
49
+ const body = css`
50
+ margin-top: 1rem;
23
51
  `;
24
52
 
25
53
  const components = {
@@ -34,44 +62,47 @@ const components = {
34
62
  }
35
63
  };
36
64
 
37
- function ContentPage(props) {
38
- const { page } = props;
65
+ function ContentPage({ page, result }) {
39
66
  return (
40
- <>
41
- <Header>
42
- <MaxWidth>
67
+ <UIMaxWidth className={container}>
68
+ {page && shouldDisplayNavigation(page) && (
69
+ <aside className={sidebar}>
70
+ <DesktopNavigation page={page} />
71
+ </aside>
72
+ )}
73
+ <article className={content}>
74
+ <Breadcrumbs
75
+ parents={result.data?.route?.parents || []}
76
+ breadcrumbs={result.data?.route?.breadcrumbs || []}
77
+ />
78
+ <h1>
43
79
  {page ? page.name : <LoadingLine heightPx={27} widthRem={15} />}
44
- </MaxWidth>
45
- </Header>
46
- <MaxWidth>
47
- {page ? (
48
- <>
49
- {page.data ? (
80
+ </h1>
81
+ <MobileNavigation page={page} />
82
+ <div className={body}>
83
+ {page ? (
84
+ page.data ? (
50
85
  <ContentRenderer
51
86
  items={page.data.items}
52
87
  components={components}
53
88
  />
54
89
  ) : (
55
- <Content
56
- dangerouslySetInnerHTML={{
57
- __html: page.content
58
- }}
59
- />
60
- )}
61
- </>
62
- ) : (
63
- <LoadingLine
64
- heightPx={14}
65
- widthRem={30}
66
- randomizeWidthBy={10}
67
- count={10}
68
- style={{
69
- marginBottom: '12px'
70
- }}
71
- />
72
- )}
73
- </MaxWidth>
74
- </>
90
+ <Content dangerouslySetInnerHTML={{ __html: page.content }} />
91
+ )
92
+ ) : (
93
+ <LoadingLine
94
+ heightPx={14}
95
+ widthRem={30}
96
+ randomizeWidthBy={10}
97
+ count={10}
98
+ style={{
99
+ marginBottom: '12px'
100
+ }}
101
+ />
102
+ )}
103
+ </div>
104
+ </article>
105
+ </UIMaxWidth>
75
106
  );
76
107
  }
77
108
 
@@ -1,4 +1,8 @@
1
1
  fragment ContentPage on Page {
2
+ primaryRoute {
3
+ id
4
+ path
5
+ }
2
6
  name
3
7
  id
4
8
  content
@@ -20,4 +24,29 @@ fragment ContentPage on Page {
20
24
  }
21
25
  }
22
26
  }
27
+
28
+ parent {
29
+ id
30
+ name
31
+ primaryRoute {
32
+ id
33
+ path
34
+ }
35
+ subPages(includeHidden: false) {
36
+ ...SubPage
37
+ }
38
+ }
39
+
40
+ subPages(includeHidden: false) {
41
+ ...SubPage
42
+ }
43
+ }
44
+
45
+ fragment SubPage on Page {
46
+ id
47
+ name
48
+ primaryRoute {
49
+ id
50
+ path
51
+ }
23
52
  }
@@ -0,0 +1,206 @@
1
+ import {
2
+ DropdownMenu,
3
+ DropdownMenuButton,
4
+ DropdownMenuItem,
5
+ DropdownMenuItems
6
+ } from '@jetshop/ui/DropdownMenu';
7
+ import React from 'react';
8
+ import { Link } from 'react-router-dom';
9
+ import { ReactComponent as Carot } from '@jetshop/ui/svg/Carrot.svg';
10
+ import { css, cx } from 'linaria';
11
+ import { theme } from '../Theme';
12
+ import { useHistory, useLocation } from 'react-router';
13
+
14
+ const menu = css`
15
+ width: 100%;
16
+
17
+ @media (min-width: 50em) {
18
+ display: none;
19
+ }
20
+
21
+ [data-flight-dropdown-button] {
22
+ width: 100%;
23
+ display: flex;
24
+ flex-direction: row;
25
+ justify-content: space-between;
26
+ align-items: center;
27
+
28
+ padding: 0.75em;
29
+ border: 1px solid #dedede;
30
+ width: 100%;
31
+ text-align: left;
32
+ background: ${theme.colors.white};
33
+ border-radius: 3px;
34
+ outline: none;
35
+
36
+ :focus {
37
+ color: ${theme.colors.blue};
38
+ }
39
+
40
+ &[aria-expanded='true'] {
41
+ border-radius: 3px 3px 0 0;
42
+ border-bottom: 0;
43
+ color: ${theme.colors.blue};
44
+
45
+ .carot {
46
+ transform: rotate(180deg);
47
+ }
48
+ }
49
+ }
50
+
51
+ [data-flight-dropdown-items] {
52
+ position: absolute;
53
+ top: 100%;
54
+ left: 0;
55
+ background: white;
56
+ display: block;
57
+ z-index: 2;
58
+ outline: none;
59
+ width: 100%;
60
+ border: 1px solid #dedede;
61
+ border-top: 0;
62
+ }
63
+
64
+ [data-flight-dropdown-item] {
65
+ line-height: 1.5;
66
+ border: none;
67
+ cursor: pointer;
68
+ white-space: normal;
69
+ padding: 0.5em 1em;
70
+ :hover {
71
+ color: blue;
72
+ }
73
+ }
74
+
75
+ [data-flight-dropdown-item] a {
76
+ text-decoration: none;
77
+ color: #000;
78
+ }
79
+
80
+ [data-flight-dropdown-item]:focus {
81
+ font-weight: bold;
82
+ background: #f2f2f2;
83
+ outline: none;
84
+ position: relative;
85
+ }
86
+
87
+ [data-flight-dropdown-item]:focus:before {
88
+ content: '';
89
+ position: absolute;
90
+ top: 0;
91
+ bottom: 0;
92
+ left: 0;
93
+ width: 2px;
94
+ background-color: #2f80ed;
95
+ }
96
+ `;
97
+
98
+ const links = css`
99
+ display: flex;
100
+ flex-direction: column;
101
+ `;
102
+
103
+ const link = css`
104
+ color: #000;
105
+ text-decoration: none;
106
+ margin-bottom: 1rem;
107
+ line-height: 1.5;
108
+ `;
109
+
110
+ const activeLink = css`
111
+ font-weight: bold;
112
+ color: #2f80ed;
113
+ `;
114
+
115
+ function getNavigationItemFromPage(page, level) {
116
+ return {
117
+ id: page.id,
118
+ name: page.name,
119
+ url: page.primaryRoute?.path,
120
+ level
121
+ };
122
+ }
123
+
124
+ function Item({ name, url, level }) {
125
+ return (
126
+ <div style={{ marginLeft: `${(level - 1) * 12}px` }}>
127
+ <Link to={url}>{name}</Link>
128
+ </div>
129
+ );
130
+ }
131
+
132
+ function getNavigationItems(page) {
133
+ const parent = page.parent ? getNavigationItemFromPage(page.parent, 1) : null;
134
+ const siblings = page.parent
135
+ ? page.parent.subPages.map(subPage => getNavigationItemFromPage(subPage, 2))
136
+ : [];
137
+ const subPages = page.subPages
138
+ ? page.subPages.map(subPage =>
139
+ getNavigationItemFromPage(subPage, parent ? 3 : 2)
140
+ )
141
+ : [];
142
+ const currentPage = getNavigationItemFromPage(page, parent ? 2 : 1);
143
+
144
+ if (!parent && !subPages?.length) return [];
145
+
146
+ const items = [];
147
+
148
+ if (parent) items.push(parent);
149
+ if (siblings.length) {
150
+ siblings.forEach(page => {
151
+ items.push(page);
152
+ if (page.id === currentPage.id) {
153
+ items.push(...subPages);
154
+ }
155
+ });
156
+ } else {
157
+ items.push(currentPage, ...subPages);
158
+ }
159
+
160
+ return items;
161
+ }
162
+
163
+ export function shouldDisplayNavigation(page) {
164
+ return Boolean(page.parent || page.subPages?.length);
165
+ }
166
+
167
+ export function DesktopNavigation({ page }) {
168
+ const location = useLocation();
169
+ const items = getNavigationItems(page);
170
+
171
+ return (
172
+ <div className={links}>
173
+ {items.map(item => (
174
+ <Link
175
+ key={item.id}
176
+ to={item.url}
177
+ className={cx(link, location.pathname === item.url && activeLink)}
178
+ style={{ marginLeft: `${(item.level - 1) * 12}px` }}
179
+ >
180
+ {item.name}
181
+ </Link>
182
+ ))}
183
+ </div>
184
+ );
185
+ }
186
+
187
+ export function MobileNavigation({ page }) {
188
+ const { push } = useHistory();
189
+ const items = page ? getNavigationItems(page) : [];
190
+
191
+ return (
192
+ <DropdownMenu className={menu}>
193
+ <DropdownMenuButton>
194
+ <span>{page?.name}</span>
195
+ <Carot className="carot" />
196
+ </DropdownMenuButton>
197
+ <DropdownMenuItems>
198
+ {items.map(item => (
199
+ <DropdownMenuItem key={item.id} onSelect={() => push(item.url)}>
200
+ <Item {...item} />
201
+ </DropdownMenuItem>
202
+ ))}
203
+ </DropdownMenuItems>
204
+ </DropdownMenu>
205
+ );
206
+ }
@@ -7,9 +7,8 @@ import { useQuery } from '@apollo/react-hooks';
7
7
 
8
8
  function FooterLinks() {
9
9
  const { data } = useQuery(pagesQuery);
10
- const { data: storeTitleData, loading: storeTitleLoading } = useQuery(
11
- StoreTitleQuery
12
- );
10
+ const { data: storeTitleData, loading: storeTitleLoading } =
11
+ useQuery(StoreTitleQuery);
13
12
 
14
13
  const pages = data?.pages;
15
14
  const storeTitle = storeTitleData?.startPage?.head.title;
@@ -20,7 +19,10 @@ function FooterLinks() {
20
19
  <ul>
21
20
  {pages.map(page => (
22
21
  <li key={page.id}>
23
- <ContentPageLink page={page}>{page.name}</ContentPageLink>
22
+ <ContentPageLink page={page}>
23
+ {page.subPages?.length ? '+ ' : null}
24
+ {page.name}
25
+ </ContentPageLink>
24
26
  </li>
25
27
  ))}
26
28
  </ul>
@@ -6,5 +6,14 @@ query PagesQuery {
6
6
  id
7
7
  path
8
8
  }
9
+
10
+ subPages {
11
+ id
12
+ name
13
+ primaryRoute {
14
+ id
15
+ path
16
+ }
17
+ }
9
18
  }
10
19
  }
@@ -143,9 +143,17 @@ const ProductToast = ({
143
143
  <ChannelContext.Consumer>
144
144
  {({ selectedChannel }) => (
145
145
  <CheckoutButton
146
- href={`${cart.externalCheckoutUrl}&channelCountry=${selectedChannel.country.code}`}
147
- onClick={() => {
148
- track(trackCartCheckoutEvent({ cart }));
146
+ href={cart.externalCheckoutUrl}
147
+ onClick={event => {
148
+ event.preventDefault();
149
+ track(
150
+ trackCartCheckoutEvent({
151
+ cart: cart,
152
+ callback: () => {
153
+ window.location = cart.externalCheckoutUrl;
154
+ }
155
+ })
156
+ );
149
157
  }}
150
158
  >
151
159
  {t('To checkout')}
@@ -80,6 +80,9 @@ const StartPageHero = ({
80
80
  src={imageSrc.value}
81
81
  className={heroStyles}
82
82
  critical={isAboveFold?.value}
83
+ focalPointY={imageSrc.value.focalPointY}
84
+ focalPointX={imageSrc.value.focalPointX}
85
+ aspect={'2:1'}
83
86
  >
84
87
  <Container>
85
88
  <Title>{header.value}</Title>
@@ -3,11 +3,16 @@
3
3
 
4
4
  fragment ContentItemFragment on ContentItem {
5
5
  type
6
- properties {
6
+ properties(getImageAsImageValue: true) {
7
7
  name
8
8
  type
9
9
  valueType
10
10
  value {
11
+ ... on ImageValue {
12
+ value
13
+ focalPointX
14
+ focalPointY
15
+ }
11
16
  ... on Product {
12
17
  ...StartPageProductGrid
13
18
  }