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 (
+
+
+
+
+ {({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 (
-
-
-
-
- {({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 (
+
+
+
+ {this.assembleTableRows().map(item => {
+ if (!item || !item.rows) {
+ return null
+ }
+
+ return (
+
+
+
+ {item.title}
+ |
+
+ {item.rows.map(row => {
+ return (
+
+ {row.title} |
+ {isUrl(row.value) ? {row.value} : row.value} |
+
+ )
+ })}
+
+ )
+ })}
+
+
+
+ )
+ }
+}
+
+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 (
-
-
-
- {this.assembleTableRows().map(item => {
- if (!item || !item.rows) {
- return null
- }
-
- return (
-
-
-
- {item.title}
- |
-
- {item.rows.map(row => {
- return (
-
- {row.title} |
- {isUrl(row.value) ? {row.value} : row.value} |
-
- )
- })}
-
- )
- })}
-
-
-
- )
- }
-}
+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 (
+
+
+
+ {isLoading && }
+ {!isLoading &&
+ users.sort(this.sortUsersByRobotStatus).map(user => {
+ const membership = project.members.find(member => member.id === user.id)
+ const media = membership.isRobot ? (
+
+ ) : (
+
+ )
+ return (
+ -
+
+
+ )
+ })}
+
+
+
+
+ )
+ }
+}
+
+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 (
-
-
-
- {isLoading && }
- {!isLoading &&
- users.sort(this.sortUsersByRobotStatus).map(user => {
- const membership = project.members.find(member => member.id === user.id)
- const media = membership.isRobot ? (
-
- ) : (
-
- )
- return (
- -
-
-
- )
- })}
-
-
-
-
- )
- }
-}
+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 (
+ <>
+
+
+ {feedItems.map(feedItem => {
+ if (!feedItem.title) {
+ return null
+ }
+ const presenter = feedItem.presenter || get(feedItem, 'guideOrTutorial.presenter') || {}
+ const date = get(feedItem, 'guideOrTutorial._createdAt')
+ return (
+ -
+
+
+ )
+ })}
+
+ >
+ )
+ }
+}
+
+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 (
- <>
-
-
- {feedItems.map(feedItem => {
- if (!feedItem.title) {
- return null
- }
- const presenter = feedItem.presenter || get(feedItem, 'guideOrTutorial.presenter') || {}
- const date = get(feedItem, 'guideOrTutorial._createdAt')
- return (
-
- )
- })}
-
- >
- )
- }
-}
+import SanityTutorials from './SanityTutorials'
export default {
name: 'sanity-tutorials',