diff --git a/packages/@sanity/base/src/styles/typography/headings.css b/packages/@sanity/base/src/styles/typography/headings.css index fe9053e65bf..c95e9ccbc2f 100644 --- a/packages/@sanity/base/src/styles/typography/headings.css +++ b/packages/@sanity/base/src/styles/typography/headings.css @@ -34,15 +34,14 @@ .heading4 { composes: root; - text-transform: uppercase; font-size: var(--font-size-h4); + /* line-height: calc(22 / 16); */ padding-top: 1rem; padding-bottom: 1rem; } .heading5 { composes: root; - text-transform: uppercase; font-size: var(--font-size-h5); padding-top: 0.5rem; padding-bottom: 0.5rem; diff --git a/packages/@sanity/dashboard/sanity.json b/packages/@sanity/dashboard/sanity.json index 9f3047b2814..8e54d49f12c 100644 --- a/packages/@sanity/dashboard/sanity.json +++ b/packages/@sanity/dashboard/sanity.json @@ -50,7 +50,7 @@ }, { "implements": "part:@sanity/dashboard/widget-styles", - "path": "styles.modules.css" + "path": "widget.css" } ] } diff --git a/packages/@sanity/dashboard/src/styles.modules.css b/packages/@sanity/dashboard/src/widget.css similarity index 95% rename from packages/@sanity/dashboard/src/styles.modules.css rename to packages/@sanity/dashboard/src/widget.css index b556ee67451..a44a924cc3e 100644 --- a/packages/@sanity/dashboard/src/styles.modules.css +++ b/packages/@sanity/dashboard/src/widget.css @@ -13,12 +13,12 @@ } .header { + padding: var(--small-padding); } .title { composes: heading4 from "part:@sanity/base/theme/typography/headings-style"; padding: var(--small-padding) var(--medium-padding) !important; - line-height: 2em; } .listContainer { diff --git a/packages/@sanity/dashboard/src/widgets/documentList/index.css b/packages/@sanity/dashboard/src/widgets/documentList/DocumentList.css similarity index 100% rename from packages/@sanity/dashboard/src/widgets/documentList/index.css rename to packages/@sanity/dashboard/src/widgets/documentList/DocumentList.css diff --git a/packages/@sanity/dashboard/src/widgets/documentList/DocumentList.js b/packages/@sanity/dashboard/src/widgets/documentList/DocumentList.js new file mode 100644 index 00000000000..0e767cc901a --- /dev/null +++ b/packages/@sanity/dashboard/src/widgets/documentList/DocumentList.js @@ -0,0 +1,117 @@ +import React from 'react' +import PropTypes from 'prop-types' +import {IntentLink} from 'part:@sanity/base/router' +import SanityPreview from 'part:@sanity/base/preview' +import QueryContainer from 'part:@sanity/base/query-container' +import Spinner from 'part:@sanity/components/loading/spinner' +import schema from 'part:@sanity/base/schema' +import IntentButton from 'part:@sanity/components/buttons/intent' +import {List, Item} from 'part:@sanity/components/lists/default' +import {intersection} from 'lodash' + +import styles from './DocumentList.css' + +const schemaTypeNames = schema.getTypeNames() + +function stringifyArray(array) { + return `["${array.join('","')}"]` +} + +class DocumentList extends React.Component { + static propTypes = { + title: PropTypes.string, + types: PropTypes.arrayOf([PropTypes.string]), + query: PropTypes.string, + order: PropTypes.string, + limit: PropTypes.number + } + + static defaultProps = { + title: 'Last created', + order: '_createdAt desc', + limit: 10, + types: null, + query: null + } + + assembleQuery = () => { + const {query, types, order, limit} = this.props + + if (query) { + return query + } + + const documentTypes = schemaTypeNames.filter(typeName => { + const schemaType = schema.get(typeName) + return schemaType.type && schemaType.type.name === 'document' + }) + + if (types) { + return ` + *[ + _type in ${stringifyArray(intersection(types, documentTypes))} + ] | order(${order}) [0...${limit}]` + } + + return ` + *[ + _type in ${stringifyArray(documentTypes)} + ] | {_id, _type} | order(${order}) [0...${limit}]` + } + + render() { + const {title, types} = this.props + const query = this.assembleQuery() + + return ( +
+
+

{title}

+
+ + + {({result, loading, error, onRetry}) => { + if (loading) { + return + } + const items = result ? result.documents : [] + return items.map(item => { + const type = schema.get(item._type) + return ( + + + + + + ) + }) + }} + + + {types && + types.length === 1 && ( +
+ + Create new {types[0]} + +
+ )} +
+ ) + } +} + +export default DocumentList diff --git a/packages/@sanity/dashboard/src/widgets/documentList/index.js b/packages/@sanity/dashboard/src/widgets/documentList/index.js index 07858a92fd5..073c8b6caf2 100644 --- a/packages/@sanity/dashboard/src/widgets/documentList/index.js +++ b/packages/@sanity/dashboard/src/widgets/documentList/index.js @@ -1,117 +1,4 @@ -import React from 'react' -import PropTypes from 'prop-types' -import {IntentLink} from 'part:@sanity/base/router' -import SanityPreview from 'part:@sanity/base/preview' -import QueryContainer from 'part:@sanity/base/query-container' -import Spinner from 'part:@sanity/components/loading/spinner' -import schema from 'part:@sanity/base/schema' -import IntentButton from 'part:@sanity/components/buttons/intent' -import {List, Item} from 'part:@sanity/components/lists/default' -import {intersection} from 'lodash' -import styles from './index.css' - -const schemaTypeNames = schema.getTypeNames() - -function stringifyArray(array) { - return `["${array.join('","')}"]` -} - -class DocumentList extends React.Component { - static propTypes = { - title: PropTypes.string, - types: PropTypes.arrayOf([PropTypes.string]), - query: PropTypes.string, - order: PropTypes.string, - limit: PropTypes.number - } - - static defaultProps = { - title: 'Last created', - order: '_createdAt desc', - limit: 10, - types: null, - query: null - } - - assembleQuery = () => { - const {query, types, order, limit} = this.props - - if (query) { - return query - } - - const documentTypes = schemaTypeNames.filter(typeName => { - const schemaType = schema.get(typeName) - return schemaType.type && schemaType.type.name === 'document' - }) - - if (types) { - return ` - *[ - _type in ${stringifyArray(intersection(types, documentTypes))} - ] | order(${order}) [0...${limit}]` - } - - return ` - *[ - _type in ${stringifyArray(documentTypes)} - ] | {_id, _type} | order(${order}) [0...${limit}]` - } - - render() { - const {title, types} = this.props - const query = this.assembleQuery() - - return ( -
-
-

{title}

-
- - - {({result, loading, error, onRetry}) => { - if (loading) { - return - } - const items = result ? result.documents : [] - return items.map(item => { - const type = schema.get(item._type) - return ( - - - - - - ) - }) - }} - - - {types && - types.length === 1 && ( -
- - Create new {types[0]} - -
- )} -
- ) - } -} +import DocumentList from './DocumentList' export default { name: 'document-list', diff --git a/packages/@sanity/dashboard/src/widgets/projectInfo/ProjectInfo.css b/packages/@sanity/dashboard/src/widgets/projectInfo/ProjectInfo.css index b4bff3e142c..79e8a9660ae 100644 --- a/packages/@sanity/dashboard/src/widgets/projectInfo/ProjectInfo.css +++ b/packages/@sanity/dashboard/src/widgets/projectInfo/ProjectInfo.css @@ -24,10 +24,18 @@ @nest & td, & th { min-width: 5em; - padding: var(--small-padding) var(--medium-padding); + padding: var(--medium-padding) var(--small-padding); } - @nest & tr:not(:last-child) { + @nest & td:first-child, & th:first-child { + padding-left: calc(var(--medium-padding) + var(--small-padding)); + } + + @nest & td:last-child, & th:last-child { + padding-right: calc(var(--medium-padding) + var(--small-padding)); + } + + @nest & tr { border-bottom: 1px solid var(--hairline-color); } @@ -37,25 +45,34 @@ } @nest & td a { - color: inherit; + color: var(--default-button-primary-color); text-decoration: inherit; + + @media (hover: hover) { + @nest &:hover { + color: inherit; + } + } } +} + +th.sectionHeader { + text-align: left; + text-transform: uppercase; + padding-left: var(--medium-padding); + padding-bottom: var(--small-padding); + padding-top: var(--medium-padding); + font-size: var(--font-size-xsmall); + font-weight: 400; + letter-spacing: 0.5px; + color: var(--text-color-secondary); + width: 100%; - @nest & .sectionHeader { - text-align: left; - text-transform: uppercase; - padding-left: var(--medium-padding); - padding-bottom: var(--small-padding); - padding-top: var(--large-padding); - font-size: var(--font-size-xsmall); - font-weight: 400; - letter-spacing: 0.5px; - color: var(--text-color-secondary); - width: 100%; + @nest .table > tbody:first-child & { + padding-top: 0; } } .buttonContainer { composes: bottomButtonContainer from 'part:@sanity/dashboard/widget-styles'; - border-top: 1px solid var(--hairline-color); } diff --git a/packages/@sanity/dashboard/src/widgets/projectInfo/ProjectInfo.js b/packages/@sanity/dashboard/src/widgets/projectInfo/ProjectInfo.js new file mode 100644 index 00000000000..10dbde7e5df --- /dev/null +++ b/packages/@sanity/dashboard/src/widgets/projectInfo/ProjectInfo.js @@ -0,0 +1,167 @@ +import React from 'react' +import PropTypes from 'prop-types' +import sanityClient from 'part:@sanity/base/client' +import getIt from 'get-it' +import jsonResponse from 'get-it/lib/middleware/jsonResponse' +import promise from 'get-it/lib/middleware/promise' +import AnchorButton from 'part:@sanity/components/buttons/anchor' +import styles from './ProjectInfo.css' + +const request = getIt([promise(), jsonResponse()]) + +function isUrl(url) { + return /^https?:\/\//.test(`${url}`) +} + +function getGraphQlUrl(projectId) { + return `https://${projectId}.api.sanity.io/v1/graphql/test` +} + +function getGroqUrl(projectId) { + return `https://${projectId}.api.sanity.io/v1/groq/test` +} + +function getManageUrl(projectId) { + return `https://manage.sanity.io/projects/${projectId}` +} + +class ProjectInfo extends React.Component { + static propTypes = { + data: PropTypes.array + } + static defaultProps = { + data: [] + } + + state = { + studioHost: null, + graphqlApi: null, + error: null + } + + componentDidMount() { + const {projectId} = sanityClient.clientConfig + // fetch project data + sanityClient.projects + .getById(projectId) + .then(result => { + const {studioHost} = result + this.setState({studioHost: studioHost ? `https://${studioHost}.sanity.studio` : null}) + }) + .catch(error => this.setState({error})) + + // ping assumed graphql endpoint + const graphqlApi = getGraphQlUrl(projectId) + request({url: graphqlApi}) + .then(response => { + this.setState({graphqlApi: response.statusCode === 200 ? graphqlApi : null}) + }) + .catch(error => this.setState({error})) + } + + assembleTableRows() { + const {projectId, dataset} = sanityClient.clientConfig + const {graphqlApi, studioHost} = this.state + const propsData = this.props.data + + let result = [ + { + title: 'Sanity project', + rows: [{title: 'Project ID', value: projectId}, {title: 'Dataset', value: dataset}] + } + ] + + // Handle any apps + const apps = [studioHost ? {title: 'Studio', value: studioHost} : null] + .concat(propsData.filter(item => item.category === 'apps')) + .filter(Boolean) + if (apps.length > 0) { + result = result.concat([{title: 'Apps', rows: apps}]) + } + + // Handle APIs + result = result.concat( + [ + { + title: 'APIs', + rows: [ + {title: 'GROQ', value: getGroqUrl(projectId)}, + {title: 'GraphQL', value: graphqlApi || 'Not deployed'} + ] + } + ], + propsData.filter(item => item.category === 'apis') + ) + + // Handle whatever else there might be + const otherStuff = {} + propsData.forEach(item => { + if (item.category !== 'apps' && item.category !== 'apis') { + if (!otherStuff[item.category]) { + otherStuff[item.category] = [] + } + otherStuff[item.category].push(item) + } + }) + + Object.keys(otherStuff).forEach(category => { + result.push({title: category}) + result = result.concat(otherStuff[category]) + }) + + return result + } + + render() { + const {error} = this.state + + if (error) { + return
{JSON.stringify(error, null, 2)}
+ } + + return ( +
+
+

Project info

+
+ + {this.assembleTableRows().map(item => { + if (!item || !item.rows) { + return null + } + + return ( + + + + + {item.rows.map(row => { + return ( + + + + + ) + })} + + ) + })} +
+ {item.title} +
{row.title}{isUrl(row.value) ? {row.value} : row.value}
+
+ + Manage project + +
+
+ ) + } +} + +export default ProjectInfo diff --git a/packages/@sanity/dashboard/src/widgets/projectInfo/index.js b/packages/@sanity/dashboard/src/widgets/projectInfo/index.js index aa0c0be45ae..2b5e41c9dde 100644 --- a/packages/@sanity/dashboard/src/widgets/projectInfo/index.js +++ b/packages/@sanity/dashboard/src/widgets/projectInfo/index.js @@ -1,168 +1,4 @@ -import React from 'react' -import PropTypes from 'prop-types' -import sanityClient from 'part:@sanity/base/client' -import getIt from 'get-it' -import jsonResponse from 'get-it/lib/middleware/jsonResponse' -import promise from 'get-it/lib/middleware/promise' -import AnchorButton from 'part:@sanity/components/buttons/anchor' -import styles from './ProjectInfo.css' - -const request = getIt([promise(), jsonResponse()]) - -function isUrl(url) { - return /^https?:\/\//.test(`${url}`) -} - -function getGraphQlUrl(projectId) { - return `https://${projectId}.api.sanity.io/v1/graphql/test` -} - -function getGroqUrl(projectId) { - return `https://${projectId}.api.sanity.io/v1/groq/test` -} - -function getManageUrl(projectId) { - return `https://manage.sanity.io/projects/${projectId}` -} - -class ProjectInfo extends React.Component { - static propTypes = { - data: PropTypes.array - } - static defaultProps = { - data: [] - } - - state = { - studioHost: null, - graphqlApi: null, - error: null - } - - componentDidMount() { - const {projectId} = sanityClient.clientConfig - // fetch project data - sanityClient.projects - .getById(projectId) - .then(result => { - const {studioHost} = result - this.setState({studioHost: studioHost ? `https://${studioHost}.sanity.studio` : null}) - }) - .catch(error => this.setState({error})) - - // ping assumed graphql endpoint - const graphqlApi = getGraphQlUrl(projectId) - request({url: graphqlApi}) - .then(response => { - this.setState({graphqlApi: response.statusCode === 200 ? graphqlApi : null}) - }) - .catch(error => this.setState({error})) - } - - assembleTableRows() { - const {projectId, dataset} = sanityClient.clientConfig - const {graphqlApi, studioHost} = this.state - const propsData = this.props.data - - let result = [ - { - title: 'Sanity project', - rows: [{title: 'Project ID', value: projectId}, {title: 'Dataset', value: dataset}] - } - ] - - // Handle any apps - const apps = [studioHost ? {title: 'Studio', value: studioHost} : null] - .concat(propsData.filter(item => item.category === 'apps')) - .filter(Boolean) - if (apps.length > 0) { - result = result.concat([{title: 'Apps', rows: apps}]) - } - - // Handle APIs - result = result.concat( - [ - { - title: 'APIs', - rows: [ - {title: 'GROQ', value: getGroqUrl(projectId)}, - {title: 'GraphQL', value: graphqlApi || 'Not deployed'} - ] - } - ], - propsData.filter(item => item.category === 'apis') - ) - - // Handle whatever else there might be - const otherStuff = {} - propsData.forEach(item => { - if (item.category !== 'apps' && item.category !== 'apis') { - if (!otherStuff[item.category]) { - otherStuff[item.category] = [] - } - otherStuff[item.category].push(item) - } - }) - - Object.keys(otherStuff).forEach(category => { - result.push({title: category}) - result = result.concat(otherStuff[category]) - }) - - return result - } - - render() { - const {error} = this.state - - if (error) { - return
{JSON.stringify(error, null, 2)}
- } - - return ( -
-
-

Project info

-
- - {this.assembleTableRows().map(item => { - if (!item || !item.rows) { - return null - } - - return ( - - - - - {item.rows.map(row => { - return ( - - - - - ) - })} - - ) - })} -
- {item.title} -
{row.title}{isUrl(row.value) ? {row.value} : row.value}
-
- - Manage project - -
-
- ) - } -} +import ProjectInfo from './ProjectInfo' export default { name: 'project-info', diff --git a/packages/@sanity/dashboard/src/widgets/projectUsers/index.css b/packages/@sanity/dashboard/src/widgets/projectUsers/ProjectUsers.css similarity index 100% rename from packages/@sanity/dashboard/src/widgets/projectUsers/index.css rename to packages/@sanity/dashboard/src/widgets/projectUsers/ProjectUsers.css diff --git a/packages/@sanity/dashboard/src/widgets/projectUsers/ProjectUsers.js b/packages/@sanity/dashboard/src/widgets/projectUsers/ProjectUsers.js new file mode 100644 index 00000000000..9f9ee516e27 --- /dev/null +++ b/packages/@sanity/dashboard/src/widgets/projectUsers/ProjectUsers.js @@ -0,0 +1,104 @@ +import React from 'react' +import sanityClient from 'part:@sanity/base/client' +import Spinner from 'part:@sanity/components/loading/spinner' +import DefaultPreview from 'part:@sanity/components/previews/default' +import {List, Item} from 'part:@sanity/components/lists/default' +import AnchorButton from 'part:@sanity/components/buttons/anchor' +import ToolIcon from 'react-icons/lib/go/tools' + +import styles from './ProjectUsers.css' + +function getInviteUrl(projectId) { + return `https://manage.sanity.io/projects/${projectId}/team/invite` +} + +class ProjectUsers extends React.Component { + static propTypes = {} + static defaultProps = {} + + state = { + project: null, + users: null, + error: null + } + + sortUsersByRobotStatus = (userA, userB) => { + const {members} = this.state.project + const membershipA = members.find(member => member.id === userA.id) + const membershipB = members.find(member => member.id === userB.id) + if (membershipA.isRobot) { + return 1 + } + if (membershipB.isRobot) { + return -1 + } + return 0 + } + + componentDidMount = () => { + const {projectId} = sanityClient.clientConfig + // fetch project data + sanityClient.projects + .getById(projectId) + .then(project => { + this.setState({project}) + sanityClient.users + .getById(project.members.map(mem => mem.id).join(',')) + .then(users => this.setState({users})) + }) + .catch(error => this.setState({error})) + } + + render() { + const {error, project, users} = this.state + + const isLoading = !project || !users + + if (error) { + return
{JSON.stringify(error, null, 2)}
+ } + + return ( +
+
+

Project users

+
+ + {isLoading && } + {!isLoading && + users.sort(this.sortUsersByRobotStatus).map(user => { + const membership = project.members.find(member => member.id === user.id) + const media = membership.isRobot ? ( + + ) : ( + + ) + return ( + + + + ) + })} + + +
+ + Invite members + +
+
+ ) + } +} + +export default ProjectUsers diff --git a/packages/@sanity/dashboard/src/widgets/projectUsers/index.js b/packages/@sanity/dashboard/src/widgets/projectUsers/index.js index 870db9bb11f..a3fc5611f55 100644 --- a/packages/@sanity/dashboard/src/widgets/projectUsers/index.js +++ b/packages/@sanity/dashboard/src/widgets/projectUsers/index.js @@ -1,105 +1,4 @@ -import React from 'react' -import sanityClient from 'part:@sanity/base/client' -import Spinner from 'part:@sanity/components/loading/spinner' -import DefaultPreview from 'part:@sanity/components/previews/default' -import {List, Item} from 'part:@sanity/components/lists/default' -import AnchorButton from 'part:@sanity/components/buttons/anchor' -import ToolIcon from 'react-icons/lib/go/tools' - -import styles from './index.css' - -function getInviteUrl(projectId) { - return `https://manage.sanity.io/projects/${projectId}/team/invite` -} - -class ProjectUsers extends React.Component { - static propTypes = {} - static defaultProps = {} - - state = { - project: null, - users: null, - error: null - } - - sortUsersByRobotStatus = (userA, userB) => { - const {members} = this.state.project - const membershipA = members.find(member => member.id === userA.id) - const membershipB = members.find(member => member.id === userB.id) - if (membershipA.isRobot) { - return 1 - } - if (membershipB.isRobot) { - return -1 - } - return 0 - } - - componentDidMount = () => { - const {projectId} = sanityClient.clientConfig - // fetch project data - sanityClient.projects - .getById(projectId) - .then(project => { - this.setState({project}) - sanityClient.users - .getById(project.members.map(mem => mem.id).join(',')) - .then(users => this.setState({users})) - }) - .catch(error => this.setState({error})) - } - - render() { - const {error, project, users} = this.state - - const isLoading = !project || !users - - if (error) { - return
{JSON.stringify(error, null, 2)}
- } - - return ( -
-
-

Project users

-
- - {isLoading && } - {!isLoading && - users.sort(this.sortUsersByRobotStatus).map(user => { - const membership = project.members.find(member => member.id === user.id) - const media = membership.isRobot ? ( - - ) : ( - - ) - return ( - - - - ) - })} - - -
- - Invite members - -
-
- ) - } -} +import ProjectUsers from './ProjectUsers' export default { name: 'project-users', diff --git a/packages/@sanity/dashboard/src/widgets/sanityTutorials/index.css b/packages/@sanity/dashboard/src/widgets/sanityTutorials/SanityTutorials.css similarity index 55% rename from packages/@sanity/dashboard/src/widgets/sanityTutorials/index.css rename to packages/@sanity/dashboard/src/widgets/sanityTutorials/SanityTutorials.css index 85c85ecbdd2..f17cfc0509d 100644 --- a/packages/@sanity/dashboard/src/widgets/sanityTutorials/index.css +++ b/packages/@sanity/dashboard/src/widgets/sanityTutorials/SanityTutorials.css @@ -8,10 +8,12 @@ composes: title from "part:@sanity/dashboard/widget-styles"; } -.tutorials { +.grid { + list-style: none; margin: 0; - padding: 0; - display: flex; - flex-wrap: nowrap; + padding: 0 var(--medium-padding) var(--medium-padding); + display: grid; + grid-gap: var(--small-padding); overflow-x: auto; + grid-template-columns: repeat(5, minmax(200px, 300px)); } diff --git a/packages/@sanity/dashboard/src/widgets/sanityTutorials/SanityTutorials.js b/packages/@sanity/dashboard/src/widgets/sanityTutorials/SanityTutorials.js new file mode 100644 index 00000000000..31ece022faf --- /dev/null +++ b/packages/@sanity/dashboard/src/widgets/sanityTutorials/SanityTutorials.js @@ -0,0 +1,89 @@ +import React from 'react' +import sanityClient from '@sanity/client' +import imageUrlBuilder from '@sanity/image-url' +import {get} from 'lodash' +import {distanceInWords} from 'date-fns' +import Tutorial from './Tutorial' + +import styles from './SanityTutorials.css' + +const client = sanityClient({ + projectId: '3do82whm', + dataset: 'production', + useCdn: true +}) + +const builder = imageUrlBuilder(client) + +const query = ` + *[_id == 'dashboardFeed-v1'] { + items[]-> { + _id, + title, + poster, + youtubeURL, + "presenter": authors[0]-> {name, mugshot, bio}, + guideOrTutorial-> { + title, + slug, + "presenter": authors[0]-> {name, mugshot, bio}, + _createdAt + } + } + }[0] +` + +function createUrl(slug) { + return `https://www.sanity.io/guide/${slug.current}` +} + +class SanityTutorials extends React.Component { + state = { + feedItems: [] + } + + componentDidMount() { + client.fetch(query).then(response => { + this.setState({ + feedItems: response.items + }) + }) + } + + render() { + const {feedItems} = this.state + return ( + <> +
+

Guides & tutorials

+
+ + + ) + } +} + +export default SanityTutorials diff --git a/packages/@sanity/dashboard/src/widgets/sanityTutorials/Tutorial.css b/packages/@sanity/dashboard/src/widgets/sanityTutorials/Tutorial.css index 4325a49e010..bfc7595df0d 100644 --- a/packages/@sanity/dashboard/src/widgets/sanityTutorials/Tutorial.css +++ b/packages/@sanity/dashboard/src/widgets/sanityTutorials/Tutorial.css @@ -7,39 +7,35 @@ cursor: pointer; display: flex; flex-direction: column; - align-items: flex-start; - padding-top: var(--medium-padding); - padding-right: var(--large-padding); - padding-left: var(--medium-padding); - padding-bottom: var(--medium-padding); -} + padding: var(--small-padding); + border-radius: 2px; -.root:visited { - opacity: 0.7; - order: -1; + &:focus { + background: var(--default-button-primary-color); + color: var(--default-button-primary-color--inverted); + } + + &:visited { + opacity: 0.7; + } } .byLine { - display: flex; margin-top: auto; - font-size: var(--font-size-small); - line-height: 1.1em; -} - -.presenter { - display: flex; - min-width: 180px; } .presenterName { + font-size: var(--font-size-small); + line-height: calc(18 / 14); font-weight: 500; margin-top: 0.25em; white-space: nowrap; } .presenterSubtitle { - color: var(--text-color-secondary); - font-size: var(--font-size-small--relative); + font-size: var(--font-size-xsmall); + line-height: calc(16 / 12); + opacity: 0.7; } .presenterAvatar { @@ -50,6 +46,7 @@ .title { font-size: inherit; + line-height: calc(22 / 16); margin: 0; padding: 0; margin-bottom: var(--small-padding); @@ -57,7 +54,7 @@ .posterContainer { width: 100%; - height: 120px; + padding-bottom: calc(9 / 16 * 100%); position: relative; background-color: var(--brand-darkest); color: var(--brand-darkest--inverted); @@ -65,9 +62,17 @@ } .poster { + position: absolute; + top: 0; + left: 0; height: 100%; - width: auto; + width: 100%; + object-fit: cover; display: block; + + @nest &:not([src]) { + display: none; + } } .playIcon { diff --git a/packages/@sanity/dashboard/src/widgets/sanityTutorials/Tutorial.js b/packages/@sanity/dashboard/src/widgets/sanityTutorials/Tutorial.js index b56dbb98087..5aa9c9dea0a 100644 --- a/packages/@sanity/dashboard/src/widgets/sanityTutorials/Tutorial.js +++ b/packages/@sanity/dashboard/src/widgets/sanityTutorials/Tutorial.js @@ -39,12 +39,8 @@ class Tutorial extends React.PureComponent {

{title}

-
-
-
{presenterName}
-
{presenterSubtitle}
-
-
+
{presenterName}
+
{presenterSubtitle}
) diff --git a/packages/@sanity/dashboard/src/widgets/sanityTutorials/index.js b/packages/@sanity/dashboard/src/widgets/sanityTutorials/index.js index c52f2dd5a37..3307a2e92b9 100644 --- a/packages/@sanity/dashboard/src/widgets/sanityTutorials/index.js +++ b/packages/@sanity/dashboard/src/widgets/sanityTutorials/index.js @@ -1,89 +1,4 @@ -import React from 'react' -import sanityClient from '@sanity/client' -import imageUrlBuilder from '@sanity/image-url' -import {get} from 'lodash' -import {distanceInWords} from 'date-fns' -import Tutorial from './Tutorial' - -import styles from './index.css' - -const client = sanityClient({ - projectId: '3do82whm', - dataset: 'production', - useCdn: true -}) - -const builder = imageUrlBuilder(client) - -const query = ` - *[_id == 'dashboardFeed-v1'] { - items[]-> { - _id, - title, - poster, - youtubeURL, - "presenter": authors[0]-> {name, mugshot, bio}, - guideOrTutorial-> { - title, - slug, - "presenter": authors[0]-> {name, mugshot, bio}, - _createdAt - } - } - }[0] -` - -function createUrl(slug) { - return `https://www.sanity.io/guide/${slug.current}` -} - -class SanityTutorials extends React.Component { - state = { - feedItems: [] - } - - componentDidMount() { - client.fetch(query).then(response => { - this.setState({ - feedItems: response.items - }) - }) - } - - render() { - const {feedItems} = this.state - return ( - <> -
-

Guides & tutorials

-
- - - ) - } -} +import SanityTutorials from './SanityTutorials' export default { name: 'sanity-tutorials',