tinacms 0.60.1 → 0.62.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.
- package/CHANGELOG.md +46 -0
- package/README.md +3 -288
- package/dist/client/index.d.ts +1 -0
- package/dist/index.es.js +53 -27
- package/dist/index.js +53 -27
- package/dist/tina-cms.d.ts +2 -0
- package/package.json +10 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# tinacms
|
|
2
2
|
|
|
3
|
+
## 0.62.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 70da62fe8: deprecated the use of `getStaticPropsForTina`
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 0afa75df1: 2342 - [TinaAdmin] Truncates the `filename` column for the Document List View
|
|
12
|
+
- 7dafce89d: Fixed issue where content creator was invalid
|
|
13
|
+
- 3de8c6165: Enabled branch creation in branch switcher
|
|
14
|
+
- fee183f8f: add "switch to default branch" recover option to error boundary
|
|
15
|
+
- 5c070a83f: feat: Add UI banner for when in localMode
|
|
16
|
+
- Updated dependencies [ddf81a4fd]
|
|
17
|
+
- Updated dependencies [20260a82d]
|
|
18
|
+
- Updated dependencies [0370147fb]
|
|
19
|
+
- Updated dependencies [3de8c6165]
|
|
20
|
+
- Updated dependencies [2eaad97bf]
|
|
21
|
+
- Updated dependencies [5c070a83f]
|
|
22
|
+
- @tinacms/toolkit@0.56.5
|
|
23
|
+
|
|
24
|
+
## 0.61.1
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [2c7718636]
|
|
29
|
+
- @tinacms/toolkit@0.56.4
|
|
30
|
+
|
|
31
|
+
## 0.61.0
|
|
32
|
+
|
|
33
|
+
### Minor Changes
|
|
34
|
+
|
|
35
|
+
- 229feda1d: add .nvmrc file for setting preferred node version
|
|
36
|
+
|
|
37
|
+
## 0.60.3
|
|
38
|
+
|
|
39
|
+
### Patch Changes
|
|
40
|
+
|
|
41
|
+
- 4adaf15af: Fix types which weren't included in previous patch
|
|
42
|
+
|
|
43
|
+
## 0.60.2
|
|
44
|
+
|
|
45
|
+
### Patch Changes
|
|
46
|
+
|
|
47
|
+
- 816271d03: Ensure login/logout pages work when admin flag is disabled
|
|
48
|
+
|
|
3
49
|
## 0.60.1
|
|
4
50
|
|
|
5
51
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,289 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
# TinaCMS
|
|
2
|
+
> The Fastest Way to Edit Next.js Content
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Getting started
|
|
6
|
-
|
|
7
|
-
Npm:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install --save-dev tinacms
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Yarn:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
yarn add --dev tinacms
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## The TinaCMS API Client
|
|
20
|
-
|
|
21
|
-
This package exports a class which acts as [TinaCMS external API](https://tina.io/docs/apis/) for the Tina Content API. This is a headless GraphQL API that's serverd via Tina Cloud or locally from within the Tina CLI.
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
import { Client, LocalClient } from "tinacms";
|
|
25
|
-
const client = new Client({
|
|
26
|
-
organizationId: "the ID you get from Tina Cloud",
|
|
27
|
-
clientId: "the client ID you get from Tina Cloud",
|
|
28
|
-
branch: "main",
|
|
29
|
-
tokenStorage: "LOCAL_STORAGE" | "MEMORY" | "CUSTOM",
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// For a simpler setup while working locally you can instantiate the LocalClient as a convenience
|
|
33
|
-
const client = new LocalClient();
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
The `Client` does a few things:
|
|
37
|
-
|
|
38
|
-
- Manages auth with Tina Cloud
|
|
39
|
-
- Provides a `request` function for working with the GraphQL API
|
|
40
|
-
|
|
41
|
-
Start by initializing the `LocalClient` - which automatically connects with your locally-running GraphQL server. From there, you can make GraphQL requests:
|
|
42
|
-
|
|
43
|
-
### `client.request`
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
const client = new LocalClient();
|
|
47
|
-
|
|
48
|
-
await client.request(
|
|
49
|
-
(gql) => gql`#graphql
|
|
50
|
-
query BlogPostQuery($relativePath: String!) {
|
|
51
|
-
{
|
|
52
|
-
getPostsDocument(relativePath: "") {
|
|
53
|
-
data {
|
|
54
|
-
title
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
`,
|
|
60
|
-
{ variables: { relativePath: "hello-world.md" } }
|
|
61
|
-
);
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
> This API currently doesn't support filtering and sorting "list" queries. We have plans to tackle that in upcoming cycles.
|
|
65
|
-
|
|
66
|
-
## `useGraphQLForms`
|
|
67
|
-
|
|
68
|
-
While GraphQL is a great tool, using it with Tina can be difficult. GraphQL can query across multiple nodes, but since each document would require its own Tina form it could be difficult to sync the data with your query with all of the forms you'd need to build. The Tina GraphQL server knows all about your content schema so we're actually able to build forms automatically by inspecting your query. To see this in action, pass your query into the `useGraphqlForms` hook:
|
|
69
|
-
|
|
70
|
-
```tsx
|
|
71
|
-
import { useGraphqlForms } from 'tinacms'
|
|
72
|
-
|
|
73
|
-
const query = gql => gql`#graphql
|
|
74
|
-
query BlogPostQuery($relativePath: String!) {
|
|
75
|
-
{
|
|
76
|
-
getPostsDocument(relativePath: $relativePath) {
|
|
77
|
-
data {
|
|
78
|
-
title
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
`
|
|
84
|
-
|
|
85
|
-
const MyPage = (props) => {
|
|
86
|
-
const [payload, isLoading] = useGraphqlForms<PostQueryResponseType>({
|
|
87
|
-
query,
|
|
88
|
-
variables: { relativePath: `${props.filename}.md` },
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
isLoading ? <div>Loading...</div> : <MyComponent {...payload}>
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
If Tina is enabled you can see a form for the `getPostsDocument` request. If you query for multiple documents, you should see multiple forms:
|
|
96
|
-
|
|
97
|
-
```tsx
|
|
98
|
-
const query = (gql) => gql`#graphql
|
|
99
|
-
query BlogPostQuery($relativePath: String!) {
|
|
100
|
-
{
|
|
101
|
-
# this generates a Tina Form
|
|
102
|
-
getSiteNavsDocument(relativePath: "site-nav.md") {
|
|
103
|
-
data {
|
|
104
|
-
items {
|
|
105
|
-
title
|
|
106
|
-
link
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
# this generates a separate Tina Form
|
|
111
|
-
getPostsDocument(relativePath: $relativePath) {
|
|
112
|
-
data {
|
|
113
|
-
title
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
`;
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### Formify
|
|
122
|
-
|
|
123
|
-
If you'd like to control the output of those forms, tap into the `formify` callback:
|
|
124
|
-
|
|
125
|
-
##### Form customization:
|
|
126
|
-
|
|
127
|
-
```tsx
|
|
128
|
-
import { useGraphqlForms } from "tinacms";
|
|
129
|
-
import { useCMS } from "tinacms";
|
|
130
|
-
|
|
131
|
-
const [payload, isLoading] = useGraphqlForms({
|
|
132
|
-
query,
|
|
133
|
-
formify: ({ formConfig, createForm, createGlobalForm, skip }) => {
|
|
134
|
-
if (formConfig.id === "getSiteNavsDocument") {
|
|
135
|
-
return createGlobalForm(formConfig);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return createForm(formConfig);
|
|
139
|
-
},
|
|
140
|
-
variables: { relativePath: `${props.filename}.md` },
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// or to skip the nav from creating a form altogether:
|
|
144
|
-
const [payload, isLoading] = useGraphqlForms({
|
|
145
|
-
query,
|
|
146
|
-
formify: ({ formConfig, createForm, skip }) => {
|
|
147
|
-
if (formConfig.id === "getSiteNavsDocument") {
|
|
148
|
-
return skip();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return createForm(formConfig);
|
|
152
|
-
},
|
|
153
|
-
variables: { relativePath: `${props.filename}.md` },
|
|
154
|
-
});
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
##### Field customization:
|
|
158
|
-
|
|
159
|
-
Since your forms are built automatically, `formify` can also be used to customize fields:
|
|
160
|
-
|
|
161
|
-
```tsx
|
|
162
|
-
const [payload, isLoading] = useGraphqlForms({
|
|
163
|
-
query,
|
|
164
|
-
formify: ({ formConfig, createForm, skip }) => {
|
|
165
|
-
return createForm({
|
|
166
|
-
...formConfig,
|
|
167
|
-
fields: formConfig.fields.map((field) => {
|
|
168
|
-
if (field.name === "title") {
|
|
169
|
-
// replace `text` with `textarea`
|
|
170
|
-
field.component = "textarea";
|
|
171
|
-
}
|
|
172
|
-
return field;
|
|
173
|
-
}),
|
|
174
|
-
});
|
|
175
|
-
},
|
|
176
|
-
variables: { relativePath: `${props.filename}.md` },
|
|
177
|
-
});
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## `useDocumentCreatorPlugin`
|
|
181
|
-
|
|
182
|
-
This hook allows your editors to safely create new pages. Note that you'll be responsible for redirecting the user after a new document has been created. To use this:
|
|
183
|
-
|
|
184
|
-
```tsx
|
|
185
|
-
import { useDocumentCreatorPlugin } from "tinacms";
|
|
186
|
-
|
|
187
|
-
// args is of type:
|
|
188
|
-
// {
|
|
189
|
-
// collection: {
|
|
190
|
-
// slug: string;
|
|
191
|
-
// };
|
|
192
|
-
// relativePath: string;
|
|
193
|
-
// breadcrumbs: string[];
|
|
194
|
-
// path: string;
|
|
195
|
-
// }
|
|
196
|
-
useDocumentCreatorPlugin((args) => window.location.assign(buildMyRouter(args)));
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Customizing the content creator options
|
|
200
|
-
|
|
201
|
-
To prevent editors from creating documents from certain collections, provide a filter function:
|
|
202
|
-
|
|
203
|
-
```tsx
|
|
204
|
-
// options are of type:
|
|
205
|
-
// {
|
|
206
|
-
// label: string;
|
|
207
|
-
// value: string;
|
|
208
|
-
// }[]
|
|
209
|
-
useDocumentCreatorPlugin(null, (options) =>
|
|
210
|
-
options.filter((option) => option.name !== "post")
|
|
211
|
-
);
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
## Authentication with Tina Cloud
|
|
215
|
-
|
|
216
|
-
While this package comes with low-level APIs for authentication with Tina Cloud, the easiest way to get started is to use the `TinaCloudAuthWall` component, which prevents children from rendering until a valid session has been established with Tina Cloud.
|
|
217
|
-
|
|
218
|
-
### `TinaCloudAuthWall`
|
|
219
|
-
|
|
220
|
-
```tsx
|
|
221
|
-
import { TinaCloudAuthWall, Client } from "tinacms";
|
|
222
|
-
|
|
223
|
-
const TinaWrapper = ({ children }) => {
|
|
224
|
-
const cms = React.useMemo(() => {
|
|
225
|
-
return new TinaCMS({
|
|
226
|
-
apis: {
|
|
227
|
-
tina: new Client({
|
|
228
|
-
// config
|
|
229
|
-
})
|
|
230
|
-
},
|
|
231
|
-
...
|
|
232
|
-
});
|
|
233
|
-
}, []);
|
|
234
|
-
|
|
235
|
-
return <TinaCloudAuthWall cms={cms}>{children}</TinaCloudAuthWall>;
|
|
236
|
-
};
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
Props for TinaCloudAuthWall
|
|
240
|
-
|
|
241
|
-
| Prop | Description |
|
|
242
|
-
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
243
|
-
| `cms` | An instance of a [CMS](http://localhost:3000/docs/cms/#cms-configuration) |
|
|
244
|
-
| `getModalActions` (optional) | A function that returns a list of actions / buttons that will be rendered to the model. Each button has name, action, and can be primary or not. The name is the text that will be displayed. The action is a function that will be run when the button is clicked. See example below for more details |
|
|
245
|
-
|
|
246
|
-
```tsx
|
|
247
|
-
return (
|
|
248
|
-
<TinaCloudAuthWall
|
|
249
|
-
cms={cms}
|
|
250
|
-
getModalActions={({ closeModal }) => {
|
|
251
|
-
return [
|
|
252
|
-
{
|
|
253
|
-
action: async () => {
|
|
254
|
-
// use your own state to get in and out of edit mode
|
|
255
|
-
closeModal();
|
|
256
|
-
},
|
|
257
|
-
name: "close",
|
|
258
|
-
primary: false,
|
|
259
|
-
},
|
|
260
|
-
];
|
|
261
|
-
}}
|
|
262
|
-
>
|
|
263
|
-
<Component {...pageProps} />
|
|
264
|
-
</TinaCloudAuthWall>
|
|
265
|
-
);
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
> Note: when using the LocalClient, TinaCloudAuthWall won't display a login screen, there is no authentication for the local GraphQL server.
|
|
269
|
-
|
|
270
|
-
### Authenticating without TinaCloudAuthWall
|
|
271
|
-
|
|
272
|
-
You can also authenticate with the `Client` directly:
|
|
273
|
-
|
|
274
|
-
```ts
|
|
275
|
-
const client = new Client({
|
|
276
|
-
// config
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
const EditSiteButton = () => {
|
|
280
|
-
const cms = useCMS();
|
|
281
|
-
const onClick = async () => {
|
|
282
|
-
await client.authenticate().then((token) => {
|
|
283
|
-
cms.enable();
|
|
284
|
-
});
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
return <button onClick={onClick}>Edit This Site</button>;
|
|
288
|
-
};
|
|
289
|
-
```
|
|
4
|
+
[Checkout the docs](https://tina.io/docs/setup-overview/) for information on how to get started
|
package/dist/client/index.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ export declare class Client {
|
|
|
42
42
|
private options;
|
|
43
43
|
events: EventBus;
|
|
44
44
|
constructor({ tokenStorage, ...options }: ServerOptions);
|
|
45
|
+
get isLocalMode(): boolean;
|
|
45
46
|
setBranch(branchName: string): void;
|
|
46
47
|
addPendingContent: (props: any) => Promise<unknown>;
|
|
47
48
|
getSchema: () => Promise<GraphQLSchema>;
|
package/dist/index.es.js
CHANGED
|
@@ -374,6 +374,11 @@ function assertIsUnionType(type) {
|
|
|
374
374
|
throw new Error(`Expected an instance of GraphQLUnionType for type ${type.name}`);
|
|
375
375
|
}
|
|
376
376
|
}
|
|
377
|
+
const captureBranchName = /^refs\/heads\/(.*)/;
|
|
378
|
+
const parseRefForBranchName = (ref) => {
|
|
379
|
+
const matches = ref.match(captureBranchName);
|
|
380
|
+
return matches[1];
|
|
381
|
+
};
|
|
377
382
|
class Client {
|
|
378
383
|
constructor(_a) {
|
|
379
384
|
var _b = _a, { tokenStorage = "MEMORY" } = _b, options = __objRest(_b, ["tokenStorage"]);
|
|
@@ -464,6 +469,9 @@ mutation addPendingDocumentMutation(
|
|
|
464
469
|
break;
|
|
465
470
|
}
|
|
466
471
|
}
|
|
472
|
+
get isLocalMode() {
|
|
473
|
+
return this.contentApiUrl.includes("localhost");
|
|
474
|
+
}
|
|
467
475
|
setBranch(branchName) {
|
|
468
476
|
var _a, _b, _c;
|
|
469
477
|
const encodedBranch = encodeURIComponent(branchName);
|
|
@@ -591,15 +599,15 @@ mutation addPendingDocumentMutation(
|
|
|
591
599
|
try {
|
|
592
600
|
const res = await this.fetchWithToken(url, {
|
|
593
601
|
method: "POST",
|
|
594
|
-
body: {
|
|
602
|
+
body: JSON.stringify({
|
|
595
603
|
baseBranch,
|
|
596
604
|
branchName
|
|
597
|
-
},
|
|
605
|
+
}),
|
|
598
606
|
headers: {
|
|
599
607
|
"Content-Type": "application/json"
|
|
600
608
|
}
|
|
601
609
|
});
|
|
602
|
-
return
|
|
610
|
+
return await res.json().then((r) => parseRefForBranchName(r.data.ref));
|
|
603
611
|
} catch (error) {
|
|
604
612
|
console.error("There was an error creating a new branch.", error);
|
|
605
613
|
return null;
|
|
@@ -1370,7 +1378,7 @@ const useDocumentCreatorPlugin = (args) => {
|
|
|
1370
1378
|
}));
|
|
1371
1379
|
};
|
|
1372
1380
|
run();
|
|
1373
|
-
}, [cms
|
|
1381
|
+
}, [cms]);
|
|
1374
1382
|
React.useEffect(() => {
|
|
1375
1383
|
if (plugin) {
|
|
1376
1384
|
cms.plugins.add(plugin);
|
|
@@ -1382,6 +1390,18 @@ const useDocumentCreatorPlugin = (args) => {
|
|
|
1382
1390
|
};
|
|
1383
1391
|
}, [plugin]);
|
|
1384
1392
|
};
|
|
1393
|
+
const errorButtonStyles = {
|
|
1394
|
+
background: "#eb6337",
|
|
1395
|
+
padding: "12px 18px",
|
|
1396
|
+
cursor: "pointer",
|
|
1397
|
+
borderRadius: "50px",
|
|
1398
|
+
textTransform: "uppercase",
|
|
1399
|
+
letterSpacing: "2px",
|
|
1400
|
+
fontWeight: "bold",
|
|
1401
|
+
border: "none",
|
|
1402
|
+
color: "white",
|
|
1403
|
+
margin: "1rem 0"
|
|
1404
|
+
};
|
|
1385
1405
|
const SetupHooks = (props) => {
|
|
1386
1406
|
const cms = useCMS();
|
|
1387
1407
|
const [payload, isLoading] = useGraphqlForms({
|
|
@@ -1411,6 +1431,8 @@ class ErrorBoundary extends React.Component {
|
|
|
1411
1431
|
return { hasError: true, message: error.message };
|
|
1412
1432
|
}
|
|
1413
1433
|
render() {
|
|
1434
|
+
const branchData = window.localStorage.getItem("tinacms-current-branch");
|
|
1435
|
+
const hasBranchData = branchData && branchData.length > 0;
|
|
1414
1436
|
if (this.state.hasError && !this.state.pageRefresh) {
|
|
1415
1437
|
return /* @__PURE__ */ React.createElement("div", {
|
|
1416
1438
|
style: {
|
|
@@ -1431,25 +1453,21 @@ class ErrorBoundary extends React.Component {
|
|
|
1431
1453
|
}
|
|
1432
1454
|
}, /* @__PURE__ */ React.createElement("h3", {
|
|
1433
1455
|
style: { color: "#eb6337" }
|
|
1434
|
-
}, "TinaCMS Render Error"), /* @__PURE__ */ React.createElement("p", null, "Tina caught an error while updating the page:"), /* @__PURE__ */ React.createElement("pre",
|
|
1435
|
-
style: {
|
|
1436
|
-
}, /* @__PURE__ */ React.createElement("button", {
|
|
1437
|
-
style:
|
|
1438
|
-
background: "#eb6337",
|
|
1439
|
-
padding: "12px 18px",
|
|
1440
|
-
cursor: "pointer",
|
|
1441
|
-
borderRadius: "50px",
|
|
1442
|
-
textTransform: "uppercase",
|
|
1443
|
-
letterSpacing: "2px",
|
|
1444
|
-
fontWeight: "bold",
|
|
1445
|
-
border: "none",
|
|
1446
|
-
color: "white"
|
|
1447
|
-
},
|
|
1456
|
+
}, "TinaCMS Render Error"), /* @__PURE__ */ React.createElement("p", null, "Tina caught an error while updating the page:"), /* @__PURE__ */ React.createElement("pre", {
|
|
1457
|
+
style: { marginTop: "1rem", overflowX: "auto" }
|
|
1458
|
+
}, this.state.message), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("p", null, `If you've just updated the form, undo your most recent changes and click "refresh". If after a few refreshes, you're still encountering this error. There is a bigger issue with the site. Please reach out to your site admin.`), /* @__PURE__ */ React.createElement("button", {
|
|
1459
|
+
style: errorButtonStyles,
|
|
1448
1460
|
onClick: () => {
|
|
1449
1461
|
this.setState({ pageRefresh: true });
|
|
1450
1462
|
setTimeout(() => this.setState({ hasError: false, pageRefresh: false }), 3e3);
|
|
1451
1463
|
}
|
|
1452
|
-
}, "Refresh"))
|
|
1464
|
+
}, "Refresh"), hasBranchData && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("p", null, `If you're using the branch switcher, you may currently be on a "stale" branch that has been deleted or whose content is not compatible with the latest version of the site's layout. Click the button below to switch back to the default branch for this deployment.`), /* @__PURE__ */ React.createElement("button", {
|
|
1465
|
+
style: errorButtonStyles,
|
|
1466
|
+
onClick: () => {
|
|
1467
|
+
window.localStorage.removeItem("tinacms-current-branch");
|
|
1468
|
+
window.location.reload();
|
|
1469
|
+
}
|
|
1470
|
+
}, "Switch to default branch"))));
|
|
1453
1471
|
}
|
|
1454
1472
|
if (this.state.pageRefresh) {
|
|
1455
1473
|
return /* @__PURE__ */ React.createElement(Loader, null, "Let's try that again.");
|
|
@@ -1973,11 +1991,11 @@ const CollectionListPage = () => {
|
|
|
1973
1991
|
className: "block text-xs mb-0.5 text-gray-400 uppercase"
|
|
1974
1992
|
}, "Filename"), /* @__PURE__ */ React.createElement(Link, {
|
|
1975
1993
|
to: `${location2.pathname}/${document2.node.sys.filename}`,
|
|
1976
|
-
className: "h-5 leading-5
|
|
1994
|
+
className: "h-5 leading-5 flex max-w-xs"
|
|
1977
1995
|
}, /* @__PURE__ */ React.createElement("span", {
|
|
1978
|
-
className: "leading-5 font-medium text-base overflow-ellipsis overflow-hidden whitespace-nowrap text-gray-700"
|
|
1996
|
+
className: "flex-shrink-1 leading-5 font-medium text-base overflow-ellipsis overflow-hidden whitespace-nowrap text-gray-700"
|
|
1979
1997
|
}, document2.node.sys.filename), /* @__PURE__ */ React.createElement("span", {
|
|
1980
|
-
className: "leading-5 text-base font-medium text-gray-300"
|
|
1998
|
+
className: "flex-shrink-0 leading-5 text-base font-medium text-gray-300"
|
|
1981
1999
|
}, document2.node.sys.extension))), /* @__PURE__ */ React.createElement("td", {
|
|
1982
2000
|
className: "px-5 py-3 whitespace-nowrap"
|
|
1983
2001
|
}, /* @__PURE__ */ React.createElement("span", {
|
|
@@ -2204,6 +2222,14 @@ const useEmbedTailwind = () => {
|
|
|
2204
2222
|
}
|
|
2205
2223
|
}, []);
|
|
2206
2224
|
};
|
|
2225
|
+
const Redirect = () => {
|
|
2226
|
+
React.useEffect(() => {
|
|
2227
|
+
if (window) {
|
|
2228
|
+
window.location.assign("/");
|
|
2229
|
+
}
|
|
2230
|
+
}, []);
|
|
2231
|
+
return null;
|
|
2232
|
+
};
|
|
2207
2233
|
const TinaAdmin = () => {
|
|
2208
2234
|
useEmbedTailwind();
|
|
2209
2235
|
const isSSR = typeof window === "undefined";
|
|
@@ -2241,11 +2267,11 @@ const TinaAdmin = () => {
|
|
|
2241
2267
|
}))))));
|
|
2242
2268
|
} else {
|
|
2243
2269
|
return /* @__PURE__ */ React.createElement(Layout, null, /* @__PURE__ */ React.createElement(BrowserRouter, null, /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, {
|
|
2244
|
-
path: "/admin/logout"
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2270
|
+
path: "/admin/logout",
|
|
2271
|
+
element: /* @__PURE__ */ React.createElement(LogoutPage, null)
|
|
2272
|
+
}), /* @__PURE__ */ React.createElement(Route, {
|
|
2273
|
+
path: "/admin",
|
|
2274
|
+
element: /* @__PURE__ */ React.createElement(Redirect, null)
|
|
2249
2275
|
}))));
|
|
2250
2276
|
}
|
|
2251
2277
|
});
|
package/dist/index.js
CHANGED
|
@@ -394,6 +394,11 @@ var __objRest = (source, exclude) => {
|
|
|
394
394
|
throw new Error(`Expected an instance of GraphQLUnionType for type ${type.name}`);
|
|
395
395
|
}
|
|
396
396
|
}
|
|
397
|
+
const captureBranchName = /^refs\/heads\/(.*)/;
|
|
398
|
+
const parseRefForBranchName = (ref) => {
|
|
399
|
+
const matches = ref.match(captureBranchName);
|
|
400
|
+
return matches[1];
|
|
401
|
+
};
|
|
397
402
|
class Client {
|
|
398
403
|
constructor(_a) {
|
|
399
404
|
var _b = _a, { tokenStorage = "MEMORY" } = _b, options = __objRest(_b, ["tokenStorage"]);
|
|
@@ -484,6 +489,9 @@ mutation addPendingDocumentMutation(
|
|
|
484
489
|
break;
|
|
485
490
|
}
|
|
486
491
|
}
|
|
492
|
+
get isLocalMode() {
|
|
493
|
+
return this.contentApiUrl.includes("localhost");
|
|
494
|
+
}
|
|
487
495
|
setBranch(branchName) {
|
|
488
496
|
var _a, _b, _c;
|
|
489
497
|
const encodedBranch = encodeURIComponent(branchName);
|
|
@@ -611,15 +619,15 @@ mutation addPendingDocumentMutation(
|
|
|
611
619
|
try {
|
|
612
620
|
const res = await this.fetchWithToken(url, {
|
|
613
621
|
method: "POST",
|
|
614
|
-
body: {
|
|
622
|
+
body: JSON.stringify({
|
|
615
623
|
baseBranch,
|
|
616
624
|
branchName
|
|
617
|
-
},
|
|
625
|
+
}),
|
|
618
626
|
headers: {
|
|
619
627
|
"Content-Type": "application/json"
|
|
620
628
|
}
|
|
621
629
|
});
|
|
622
|
-
return
|
|
630
|
+
return await res.json().then((r) => parseRefForBranchName(r.data.ref));
|
|
623
631
|
} catch (error) {
|
|
624
632
|
console.error("There was an error creating a new branch.", error);
|
|
625
633
|
return null;
|
|
@@ -1390,7 +1398,7 @@ mutation addPendingDocumentMutation(
|
|
|
1390
1398
|
}));
|
|
1391
1399
|
};
|
|
1392
1400
|
run();
|
|
1393
|
-
}, [cms
|
|
1401
|
+
}, [cms]);
|
|
1394
1402
|
React__default["default"].useEffect(() => {
|
|
1395
1403
|
if (plugin) {
|
|
1396
1404
|
cms.plugins.add(plugin);
|
|
@@ -1402,6 +1410,18 @@ mutation addPendingDocumentMutation(
|
|
|
1402
1410
|
};
|
|
1403
1411
|
}, [plugin]);
|
|
1404
1412
|
};
|
|
1413
|
+
const errorButtonStyles = {
|
|
1414
|
+
background: "#eb6337",
|
|
1415
|
+
padding: "12px 18px",
|
|
1416
|
+
cursor: "pointer",
|
|
1417
|
+
borderRadius: "50px",
|
|
1418
|
+
textTransform: "uppercase",
|
|
1419
|
+
letterSpacing: "2px",
|
|
1420
|
+
fontWeight: "bold",
|
|
1421
|
+
border: "none",
|
|
1422
|
+
color: "white",
|
|
1423
|
+
margin: "1rem 0"
|
|
1424
|
+
};
|
|
1405
1425
|
const SetupHooks = (props) => {
|
|
1406
1426
|
const cms = toolkit.useCMS();
|
|
1407
1427
|
const [payload, isLoading] = useGraphqlForms({
|
|
@@ -1431,6 +1451,8 @@ mutation addPendingDocumentMutation(
|
|
|
1431
1451
|
return { hasError: true, message: error.message };
|
|
1432
1452
|
}
|
|
1433
1453
|
render() {
|
|
1454
|
+
const branchData = window.localStorage.getItem("tinacms-current-branch");
|
|
1455
|
+
const hasBranchData = branchData && branchData.length > 0;
|
|
1434
1456
|
if (this.state.hasError && !this.state.pageRefresh) {
|
|
1435
1457
|
return /* @__PURE__ */ React__default["default"].createElement("div", {
|
|
1436
1458
|
style: {
|
|
@@ -1451,25 +1473,21 @@ mutation addPendingDocumentMutation(
|
|
|
1451
1473
|
}
|
|
1452
1474
|
}, /* @__PURE__ */ React__default["default"].createElement("h3", {
|
|
1453
1475
|
style: { color: "#eb6337" }
|
|
1454
|
-
}, "TinaCMS Render Error"), /* @__PURE__ */ React__default["default"].createElement("p", null, "Tina caught an error while updating the page:"), /* @__PURE__ */ React__default["default"].createElement("pre",
|
|
1455
|
-
style: {
|
|
1456
|
-
}, /* @__PURE__ */ React__default["default"].createElement("button", {
|
|
1457
|
-
style:
|
|
1458
|
-
background: "#eb6337",
|
|
1459
|
-
padding: "12px 18px",
|
|
1460
|
-
cursor: "pointer",
|
|
1461
|
-
borderRadius: "50px",
|
|
1462
|
-
textTransform: "uppercase",
|
|
1463
|
-
letterSpacing: "2px",
|
|
1464
|
-
fontWeight: "bold",
|
|
1465
|
-
border: "none",
|
|
1466
|
-
color: "white"
|
|
1467
|
-
},
|
|
1476
|
+
}, "TinaCMS Render Error"), /* @__PURE__ */ React__default["default"].createElement("p", null, "Tina caught an error while updating the page:"), /* @__PURE__ */ React__default["default"].createElement("pre", {
|
|
1477
|
+
style: { marginTop: "1rem", overflowX: "auto" }
|
|
1478
|
+
}, this.state.message), /* @__PURE__ */ React__default["default"].createElement("br", null), /* @__PURE__ */ React__default["default"].createElement("p", null, `If you've just updated the form, undo your most recent changes and click "refresh". If after a few refreshes, you're still encountering this error. There is a bigger issue with the site. Please reach out to your site admin.`), /* @__PURE__ */ React__default["default"].createElement("button", {
|
|
1479
|
+
style: errorButtonStyles,
|
|
1468
1480
|
onClick: () => {
|
|
1469
1481
|
this.setState({ pageRefresh: true });
|
|
1470
1482
|
setTimeout(() => this.setState({ hasError: false, pageRefresh: false }), 3e3);
|
|
1471
1483
|
}
|
|
1472
|
-
}, "Refresh"))
|
|
1484
|
+
}, "Refresh"), hasBranchData && /* @__PURE__ */ React__default["default"].createElement(React__default["default"].Fragment, null, /* @__PURE__ */ React__default["default"].createElement("p", null, `If you're using the branch switcher, you may currently be on a "stale" branch that has been deleted or whose content is not compatible with the latest version of the site's layout. Click the button below to switch back to the default branch for this deployment.`), /* @__PURE__ */ React__default["default"].createElement("button", {
|
|
1485
|
+
style: errorButtonStyles,
|
|
1486
|
+
onClick: () => {
|
|
1487
|
+
window.localStorage.removeItem("tinacms-current-branch");
|
|
1488
|
+
window.location.reload();
|
|
1489
|
+
}
|
|
1490
|
+
}, "Switch to default branch"))));
|
|
1473
1491
|
}
|
|
1474
1492
|
if (this.state.pageRefresh) {
|
|
1475
1493
|
return /* @__PURE__ */ React__default["default"].createElement(Loader, null, "Let's try that again.");
|
|
@@ -1993,11 +2011,11 @@ This will work when developing locally but NOT when deployed to production.
|
|
|
1993
2011
|
className: "block text-xs mb-0.5 text-gray-400 uppercase"
|
|
1994
2012
|
}, "Filename"), /* @__PURE__ */ React__default["default"].createElement(reactRouterDom.Link, {
|
|
1995
2013
|
to: `${location2.pathname}/${document2.node.sys.filename}`,
|
|
1996
|
-
className: "h-5 leading-5
|
|
2014
|
+
className: "h-5 leading-5 flex max-w-xs"
|
|
1997
2015
|
}, /* @__PURE__ */ React__default["default"].createElement("span", {
|
|
1998
|
-
className: "leading-5 font-medium text-base overflow-ellipsis overflow-hidden whitespace-nowrap text-gray-700"
|
|
2016
|
+
className: "flex-shrink-1 leading-5 font-medium text-base overflow-ellipsis overflow-hidden whitespace-nowrap text-gray-700"
|
|
1999
2017
|
}, document2.node.sys.filename), /* @__PURE__ */ React__default["default"].createElement("span", {
|
|
2000
|
-
className: "leading-5 text-base font-medium text-gray-300"
|
|
2018
|
+
className: "flex-shrink-0 leading-5 text-base font-medium text-gray-300"
|
|
2001
2019
|
}, document2.node.sys.extension))), /* @__PURE__ */ React__default["default"].createElement("td", {
|
|
2002
2020
|
className: "px-5 py-3 whitespace-nowrap"
|
|
2003
2021
|
}, /* @__PURE__ */ React__default["default"].createElement("span", {
|
|
@@ -2224,6 +2242,14 @@ This will work when developing locally but NOT when deployed to production.
|
|
|
2224
2242
|
}
|
|
2225
2243
|
}, []);
|
|
2226
2244
|
};
|
|
2245
|
+
const Redirect = () => {
|
|
2246
|
+
React__default["default"].useEffect(() => {
|
|
2247
|
+
if (window) {
|
|
2248
|
+
window.location.assign("/");
|
|
2249
|
+
}
|
|
2250
|
+
}, []);
|
|
2251
|
+
return null;
|
|
2252
|
+
};
|
|
2227
2253
|
const TinaAdmin = () => {
|
|
2228
2254
|
useEmbedTailwind();
|
|
2229
2255
|
const isSSR = typeof window === "undefined";
|
|
@@ -2261,11 +2287,11 @@ This will work when developing locally but NOT when deployed to production.
|
|
|
2261
2287
|
}))))));
|
|
2262
2288
|
} else {
|
|
2263
2289
|
return /* @__PURE__ */ React__default["default"].createElement(Layout, null, /* @__PURE__ */ React__default["default"].createElement(reactRouterDom.BrowserRouter, null, /* @__PURE__ */ React__default["default"].createElement(reactRouterDom.Routes, null, /* @__PURE__ */ React__default["default"].createElement(reactRouterDom.Route, {
|
|
2264
|
-
path: "/admin/logout"
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2290
|
+
path: "/admin/logout",
|
|
2291
|
+
element: /* @__PURE__ */ React__default["default"].createElement(LogoutPage, null)
|
|
2292
|
+
}), /* @__PURE__ */ React__default["default"].createElement(reactRouterDom.Route, {
|
|
2293
|
+
path: "/admin",
|
|
2294
|
+
element: /* @__PURE__ */ React__default["default"].createElement(Redirect, null)
|
|
2269
2295
|
}))));
|
|
2270
2296
|
}
|
|
2271
2297
|
});
|
package/dist/tina-cms.d.ts
CHANGED
|
@@ -42,6 +42,8 @@ export declare const TinaCMSProvider2: ({ children, branch, clientId, isLocalCli
|
|
|
42
42
|
tinaioConfig?: TinaIOConfig;
|
|
43
43
|
}) => JSX.Element;
|
|
44
44
|
/**
|
|
45
|
+
* @deprecated v0.62.0: Use `staticRequest` and a "try catch" block instead. see https://tina.io/docs/features/data-fetching/#querying-tina-content-in-nextjs for more details
|
|
46
|
+
*
|
|
45
47
|
* A convenience function which makes a GraphQL request
|
|
46
48
|
* to a local GraphQL server and ensures the response fits
|
|
47
49
|
* the shape expected by Tina context in your application
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tinacms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.62.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -16,13 +16,14 @@
|
|
|
16
16
|
"license": "Apache-2.0",
|
|
17
17
|
"scripts": {
|
|
18
18
|
"types": "yarn tsc",
|
|
19
|
-
"build": "echo \"Run `yarn build` from the root of the repository instead\""
|
|
19
|
+
"build": "echo \"Run `yarn build` from the root of the repository instead\"",
|
|
20
|
+
"test": "jest --env=jsdom --passWithNoTests"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
22
23
|
"@headlessui/react": "^1.4.1",
|
|
23
24
|
"@heroicons/react": "^1.0.4",
|
|
24
25
|
"@tinacms/sharedctx": "0.0.1",
|
|
25
|
-
"@tinacms/toolkit": "0.56.
|
|
26
|
+
"@tinacms/toolkit": "0.56.5",
|
|
26
27
|
"crypto-js": "^4.0.0",
|
|
27
28
|
"final-form": "4.20.1",
|
|
28
29
|
"graphql": "^15.1.0",
|
|
@@ -33,14 +34,20 @@
|
|
|
33
34
|
"yup": "^0.32.0"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
37
|
+
"@testing-library/jest-dom": "^5.11.9",
|
|
38
|
+
"@testing-library/react": "^12.0.0",
|
|
39
|
+
"@testing-library/user-event": "^12.7.0",
|
|
36
40
|
"@tinacms/scripts": "0.50.4",
|
|
41
|
+
"@types/jest": "^27.0.1",
|
|
37
42
|
"@types/lodash": "^4.14.169",
|
|
38
43
|
"@types/node": "^14.0.13",
|
|
39
44
|
"@types/react": "^16.9.38",
|
|
40
45
|
"@types/yup": "^0.29.10",
|
|
46
|
+
"jest": "^27.0.6",
|
|
41
47
|
"next": "9.4.2",
|
|
42
48
|
"react": "16.14.0",
|
|
43
49
|
"react-dom": "16.14.0",
|
|
50
|
+
"react-is": "^17.0.2",
|
|
44
51
|
"styled-components": "^5.2.0",
|
|
45
52
|
"typescript": "^4.3.5"
|
|
46
53
|
},
|