├── .nvmrc
├── stories
├── Image
│ ├── README.md
│ └── image.stories.js
├── ShexForm
│ ├── README.md
│ └── shex-form.stories.js
├── ProfileUploader
│ ├── README.md
│ └── profile-uploader.stories.js
├── LogoutButton
│ ├── README.md
│ └── logout-button.stories.js
├── withAuthorization
│ ├── README.md
│ └── with-authorization.stories.js
├── withWebId
│ ├── README.md
│ └── with-webid.stories.js
├── PrivateRoute
│ ├── private-route.stories.js
│ └── README.md
├── Uploader
│ ├── uploader.stories.js
│ └── README.md
└── ProviderLogin
│ ├── provider-login.stories.js
│ └── README.md
├── src
├── lib
│ ├── components
│ │ ├── FormModel
│ │ │ ├── children
│ │ │ │ ├── Form
│ │ │ │ │ └── UI
│ │ │ │ │ │ ├── RadioButtonList
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── radio-button-list.component.js
│ │ │ │ │ │ └── radio-button-list.test.js
│ │ │ │ │ │ ├── Classifier
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── classifier.style.js
│ │ │ │ │ │ └── classifier.component.js
│ │ │ │ │ │ ├── Email
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── email.test.js
│ │ │ │ │ │ └── email.component.js
│ │ │ │ │ │ ├── Float
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── float.test.js
│ │ │ │ │ │ └── float.component.js
│ │ │ │ │ │ ├── Input
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── input.styles.js
│ │ │ │ │ │ ├── input.test.js
│ │ │ │ │ │ └── input.component.js
│ │ │ │ │ │ ├── Phone
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── phone.test.js
│ │ │ │ │ │ └── phone.component.js
│ │ │ │ │ │ ├── Select
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── select.component.js
│ │ │ │ │ │ └── select.test.js
│ │ │ │ │ │ ├── Comment
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── comment.component.js
│ │ │ │ │ │ └── comment.test.js
│ │ │ │ │ │ ├── Decimal
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── decimal.test.js
│ │ │ │ │ │ └── decimal.component.js
│ │ │ │ │ │ ├── Heading
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── heading.component.js
│ │ │ │ │ │ └── heading.test.js
│ │ │ │ │ │ ├── Integer
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── integer.test.js
│ │ │ │ │ │ └── integer.component.js
│ │ │ │ │ │ ├── CheckBox
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── check-box.styles.js
│ │ │ │ │ │ ├── check-box.test.js
│ │ │ │ │ │ └── check-box.component.js
│ │ │ │ │ │ ├── TextArea
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── text-area.styles.js
│ │ │ │ │ │ ├── text-area.test.js
│ │ │ │ │ │ └── text-area.component.js
│ │ │ │ │ │ ├── RadioButton
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── radio-button.component.js
│ │ │ │ │ │ └── radio-button.test.js
│ │ │ │ │ │ ├── CheckBoxList
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── check-box-list.component.js
│ │ │ │ │ │ └── check-box-list.test.js
│ │ │ │ │ │ ├── DeleteButton
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── delete-button.component.js
│ │ │ │ │ │ └── delete-button.test.js
│ │ │ │ │ │ ├── ErrorMessage
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── error-message.styled.js
│ │ │ │ │ │ └── error-message.component.js
│ │ │ │ │ │ ├── DateTimePicker
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── date-time.styles.js
│ │ │ │ │ │ └── date-time-picker.test.js
│ │ │ │ │ │ ├── ColorPicker
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── color-picker.styles.js
│ │ │ │ │ │ ├── color-picker.test.js
│ │ │ │ │ │ └── color-picker.component.js
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── component-mapping.js
│ │ │ │ ├── Viewer
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── UI
│ │ │ │ │ │ ├── DateLine
│ │ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ │ ├── date-line.style.js
│ │ │ │ │ │ │ ├── date-line.component.js
│ │ │ │ │ │ │ └── date-line.test.js
│ │ │ │ │ │ ├── ColorLine
│ │ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ │ ├── color-line.style.js
│ │ │ │ │ │ │ ├── color-line.component.js
│ │ │ │ │ │ │ └── color-line.test.js
│ │ │ │ │ │ ├── MultiLine
│ │ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ │ ├── multi-line.style.js
│ │ │ │ │ │ │ ├── multi-line.component.js
│ │ │ │ │ │ │ └── multi-line.test.js
│ │ │ │ │ │ ├── SingleLine
│ │ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ │ ├── single-line.style.js
│ │ │ │ │ │ │ ├── single-line.component.js
│ │ │ │ │ │ │ └── single-line.test.js
│ │ │ │ │ │ ├── BoolLine
│ │ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ │ ├── bool-line.component.js
│ │ │ │ │ │ │ ├── bool-line.test.js
│ │ │ │ │ │ │ └── bool-line.style.js
│ │ │ │ │ │ ├── MultipleViewer
│ │ │ │ │ │ │ └── multiple-viewer.component.js
│ │ │ │ │ │ └── ui-mapping.js
│ │ │ │ │ ├── viewer.style.js
│ │ │ │ │ └── viewer.component.js
│ │ │ │ ├── Group
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── group.style.js
│ │ │ │ │ └── group.component.js
│ │ │ │ ├── Multiple
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── multiple.component.js
│ │ │ │ └── Spinner
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── spinner.component.js
│ │ │ ├── index.js
│ │ │ └── live-form-model.component.js
│ │ ├── ShexForm
│ │ │ ├── index.js
│ │ │ ├── children
│ │ │ │ ├── Field
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── field.component.test.js
│ │ │ │ │ └── field.component.js
│ │ │ │ ├── AddButton
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── add-button.component.js
│ │ │ │ │ └── add-button.component.test.js
│ │ │ │ ├── InputField
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── styled.component.js
│ │ │ │ │ ├── input-field.component.test.js
│ │ │ │ │ └── input-field.component.js
│ │ │ │ ├── DeleteButton
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── delete-button.component.js
│ │ │ │ │ └── delete-button.component.test.js
│ │ │ │ ├── DropDownField
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── styled.component.js
│ │ │ │ │ ├── dropdown-field.component.test.js
│ │ │ │ │ └── dropdown-field.component.js
│ │ │ │ ├── index.js
│ │ │ │ └── expression-fields.component.js
│ │ │ ├── styled.component.js
│ │ │ └── shex-form.component.js
│ │ ├── Uploader
│ │ │ ├── index.js
│ │ │ └── uploader.test.js
│ │ ├── ProfileViewer
│ │ │ ├── index.js
│ │ │ ├── profile-viewer.style.js
│ │ │ └── profile-viewer.component.js
│ │ ├── ProviderLogin
│ │ │ ├── children
│ │ │ │ └── Form
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── form.presentational.js
│ │ │ ├── index.js
│ │ │ └── provider-login.container.test.js
│ │ ├── ProfileUploader
│ │ │ ├── index.js
│ │ │ ├── profile-uploader.style.js
│ │ │ ├── profile-uploader.test.js
│ │ │ └── profile-uploader.component.js
│ │ ├── ProviderSelect
│ │ │ ├── index.js
│ │ │ ├── provider.select.component.test.js
│ │ │ ├── styled.components.js
│ │ │ └── provider.select.component.js
│ │ ├── ShexFormBuilder
│ │ │ ├── index.js
│ │ │ ├── children
│ │ │ │ └── ShexFormLive
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── styled.component.js
│ │ │ │ │ └── shex-form-live.component.js
│ │ │ ├── shex-form-builder.component.test.js
│ │ │ └── shex-form-builder.component.js
│ │ ├── PrivateRoute
│ │ │ ├── index.js
│ │ │ ├── private-route.style.js
│ │ │ ├── private-route.component.js
│ │ │ └── private-route.component.test.js
│ │ ├── withAuthorization
│ │ │ ├── index.js
│ │ │ ├── with-authorization.component.js
│ │ │ └── with-authorization.test.js
│ │ └── index.js
│ ├── utils
│ │ ├── statusMessage.js
│ │ ├── error.js
│ │ ├── index.js
│ │ ├── datestimes.test.js
│ │ ├── datetimes.js
│ │ ├── shexFormValidator.js
│ │ └── solidFetch.js
│ ├── styled-components
│ │ ├── index.js
│ │ └── form.js
│ ├── context
│ │ ├── ThemeContext.js
│ │ ├── shex.provider.js
│ │ ├── formModel.provider.js
│ │ └── index.js
│ ├── hooks
│ │ ├── index.js
│ │ ├── useShex.test.js
│ │ └── useNotification.test.js
│ ├── classes
│ │ ├── index.js
│ │ ├── access-control-factory.js
│ │ ├── access-control-list.test.js
│ │ ├── notifications.test.js
│ │ └── app-permissions.js
│ ├── constants
│ │ └── index.js
│ ├── index.js
│ ├── entities
│ │ └── index.js
│ └── shapes
│ │ └── notification.json
├── assets
│ ├── inrupt_logo.png
│ └── solid_logo.png
├── demo
│ ├── components
│ │ ├── index.js
│ │ └── HandleShexForm
│ │ │ ├── index.js
│ │ │ └── handle-shex-form.component.js
│ └── index.js
└── test
│ └── __mocks__
│ ├── @solid
│ └── query-ldflex.js
│ └── solid-auth-client.js
├── .prettierrc
├── .travis.yml
├── public
├── favicon.ico
├── manifest.json
└── index.html
├── .storybook
├── addons.js
└── config.js
├── CONTRIBUTING.md
├── config
├── jest
│ ├── fileTransform.js
│ └── cssTransform.js
├── polyfills.js
├── paths.js
└── env.js
├── .gitignore
├── scripts
├── test.js
└── start.js
├── .eslintrc
└── LICENSE
/.nvmrc:
--------------------------------------------------------------------------------
1 | v12.7.0
2 |
--------------------------------------------------------------------------------
/stories/Image/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/stories/ShexForm/README.md:
--------------------------------------------------------------------------------
1 | # Shex Form Component
2 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/RadioButtonList/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "printWidth": 100
4 | }
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 'node'
4 | script:
5 | - npm run test
6 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inrupt/solid-react-components/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './viewer.component';
2 |
--------------------------------------------------------------------------------
/src/assets/inrupt_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inrupt/solid-react-components/HEAD/src/assets/inrupt_logo.png
--------------------------------------------------------------------------------
/src/assets/solid_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inrupt/solid-react-components/HEAD/src/assets/solid_logo.png
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/DateLine/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './date-line.component';
2 |
--------------------------------------------------------------------------------
/src/demo/components/index.js:
--------------------------------------------------------------------------------
1 | import HandleShexForm from './HandleShexForm';
2 |
3 | export default HandleShexForm;
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Classifier/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './classifier.component';
2 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/ColorLine/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './color-line.component';
2 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/MultiLine/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './multi-line.component';
2 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/index.js:
--------------------------------------------------------------------------------
1 | import ShexForm from './shex-form.component';
2 |
3 | export default ShexForm;
4 |
--------------------------------------------------------------------------------
/src/lib/components/Uploader/index.js:
--------------------------------------------------------------------------------
1 | import Uploader from './uploader.component';
2 |
3 | export default Uploader;
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Group/index.js:
--------------------------------------------------------------------------------
1 | import { Group } from './group.component';
2 |
3 | export { Group };
4 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/Field/index.js:
--------------------------------------------------------------------------------
1 | import Field from './field.component';
2 |
3 | export default Field;
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Email/index.js:
--------------------------------------------------------------------------------
1 | import { Email } from './email.component';
2 |
3 | export { Email };
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Float/index.js:
--------------------------------------------------------------------------------
1 | import { Float } from './float.component';
2 |
3 | export { Float };
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Input/index.js:
--------------------------------------------------------------------------------
1 | import { Input } from './input.component';
2 |
3 | export { Input };
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Phone/index.js:
--------------------------------------------------------------------------------
1 | import { Phone } from './phone.component';
2 |
3 | export { Phone };
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Select/index.js:
--------------------------------------------------------------------------------
1 | import Select from './select.component';
2 |
3 | export default Select;
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Multiple/index.js:
--------------------------------------------------------------------------------
1 | import { Multiple } from './multiple.component';
2 |
3 | export { Multiple };
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Comment/index.js:
--------------------------------------------------------------------------------
1 | import { Comment } from './comment.component';
2 |
3 | export { Comment };
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Decimal/index.js:
--------------------------------------------------------------------------------
1 | import { Decimal } from './decimal.component';
2 |
3 | export { Decimal };
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Heading/index.js:
--------------------------------------------------------------------------------
1 | import { Heading } from './heading.component';
2 |
3 | export { Heading };
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Integer/index.js:
--------------------------------------------------------------------------------
1 | import { Integer } from './integer.component';
2 |
3 | export { Integer };
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Spinner/index.js:
--------------------------------------------------------------------------------
1 |
2 | import { Spinner } from './spinner.component';
3 |
4 | export { Spinner };
5 |
--------------------------------------------------------------------------------
/src/lib/components/ProfileViewer/index.js:
--------------------------------------------------------------------------------
1 | import ProfileViewer from './profile-viewer.component';
2 |
3 | export default ProfileViewer;
4 |
--------------------------------------------------------------------------------
/src/lib/components/ProviderLogin/children/Form/index.js:
--------------------------------------------------------------------------------
1 | import LoginForm from './form.presentational';
2 |
3 | export default LoginForm;
4 |
--------------------------------------------------------------------------------
/src/lib/components/ProviderLogin/index.js:
--------------------------------------------------------------------------------
1 | import ProviderLogin from './provider-login.container';
2 |
3 | export default ProviderLogin;
4 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/AddButton/index.js:
--------------------------------------------------------------------------------
1 | import AddButton from './add-button.component';
2 |
3 | export default AddButton;
4 |
--------------------------------------------------------------------------------
/src/demo/components/HandleShexForm/index.js:
--------------------------------------------------------------------------------
1 | import HandleShexForm from './handle-shex-form.component';
2 |
3 | export default HandleShexForm;
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/CheckBox/index.js:
--------------------------------------------------------------------------------
1 | import { CheckBox } from './check-box.component';
2 |
3 | export { CheckBox };
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/TextArea/index.js:
--------------------------------------------------------------------------------
1 | import { TextArea } from './text-area.component';
2 |
3 | export { TextArea };
4 |
--------------------------------------------------------------------------------
/src/lib/components/ProfileUploader/index.js:
--------------------------------------------------------------------------------
1 | import ProfileUploader from './profile-uploader.component';
2 |
3 | export default ProfileUploader;
4 |
--------------------------------------------------------------------------------
/src/lib/components/ProviderSelect/index.js:
--------------------------------------------------------------------------------
1 | import ProviderSelect from './provider.select.component';
2 |
3 | export default ProviderSelect;
4 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/InputField/index.js:
--------------------------------------------------------------------------------
1 | import InputField from './input-field.component';
2 |
3 | export default InputField;
4 |
--------------------------------------------------------------------------------
/src/lib/components/ShexFormBuilder/index.js:
--------------------------------------------------------------------------------
1 | import ShexFormBuilder from './shex-form-builder.component';
2 |
3 | export default ShexFormBuilder;
4 |
--------------------------------------------------------------------------------
/src/lib/utils/statusMessage.js:
--------------------------------------------------------------------------------
1 | export default function solidResponse(code, message, optionals) {
2 | return { code, message, ...optionals };
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/SingleLine/index.js:
--------------------------------------------------------------------------------
1 | import { SingleLine } from './single-line.component';
2 |
3 | export { SingleLine };
4 |
--------------------------------------------------------------------------------
/src/lib/components/PrivateRoute/index.js:
--------------------------------------------------------------------------------
1 | import PrivateRouteWithWebId from './private-route.component';
2 |
3 | export default PrivateRouteWithWebId;
4 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/DeleteButton/index.js:
--------------------------------------------------------------------------------
1 | import DeleteButton from './delete-button.component';
2 |
3 | export default DeleteButton;
4 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/DropDownField/index.js:
--------------------------------------------------------------------------------
1 | import DropDownField from './dropdown-field.component';
2 |
3 | export default DropDownField;
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/RadioButton/index.js:
--------------------------------------------------------------------------------
1 | import { RadioButton } from './radio-button.component';
2 |
3 | export { RadioButton };
4 |
--------------------------------------------------------------------------------
/src/lib/components/ShexFormBuilder/children/ShexFormLive/index.js:
--------------------------------------------------------------------------------
1 | import ShexFormLive from './shex-form-live.component';
2 |
3 | export default ShexFormLive;
4 |
--------------------------------------------------------------------------------
/src/lib/components/withAuthorization/index.js:
--------------------------------------------------------------------------------
1 | import { withAuthorization } from './with-authorization.component';
2 |
3 | export default withAuthorization;
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/CheckBoxList/index.js:
--------------------------------------------------------------------------------
1 | import CheckBoxList from './check-box-list.component';
2 |
3 | export default CheckBoxList;
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/DeleteButton/index.js:
--------------------------------------------------------------------------------
1 | import { DeleteButton } from './delete-button.component';
2 |
3 | export default DeleteButton;
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/ErrorMessage/index.js:
--------------------------------------------------------------------------------
1 | import { ErrorMessage } from './error-message.component';
2 |
3 | export default ErrorMessage;
4 |
--------------------------------------------------------------------------------
/stories/ProfileUploader/README.md:
--------------------------------------------------------------------------------
1 | ### ProfileUploader
2 |
3 | Here's an example of a basic ProfileUploader component, demonstrating how to add your own Uploader UI
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/DateTimePicker/index.js:
--------------------------------------------------------------------------------
1 | import { DateTimePicker } from './date-time-picker.component';
2 |
3 | export { DateTimePicker };
4 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/BoolLine/index.js:
--------------------------------------------------------------------------------
1 | import { BoolLine } from './bool-line.component';
2 |
3 | export default BoolLine;
4 | export { BoolLine };
5 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/index.js:
--------------------------------------------------------------------------------
1 | import { FormModel } from './form-model.component';
2 | import { Spinner } from './children/Spinner';
3 |
4 | export { FormModel, Spinner };
5 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/ColorPicker/index.js:
--------------------------------------------------------------------------------
1 | import ColorPicker from './color-picker.component';
2 |
3 | export default ColorPicker;
4 | export { ColorPicker };
5 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Select/select.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Select = () =>
Select
;
4 |
5 | export default Select;
6 |
--------------------------------------------------------------------------------
/src/lib/styled-components/index.js:
--------------------------------------------------------------------------------
1 | import { SolidInput, SolidLinkButton, SolidButton, ErrorMessage } from './form';
2 |
3 | export { SolidInput, SolidLinkButton, SolidButton, ErrorMessage };
4 |
--------------------------------------------------------------------------------
/src/lib/context/ThemeContext.js:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react';
2 |
3 | const defaultContext = {
4 | theme: {}
5 | };
6 |
7 | export const ThemeContext = createContext(defaultContext);
8 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Group/group.style.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const ComponentWrapper = styled.div`
4 | display: flex;
5 | align-items: center;
6 | `;
7 |
--------------------------------------------------------------------------------
/src/lib/components/PrivateRoute/private-route.style.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Loader = styled.div`
4 | max-width: 300;
5 | margin: 0 auto;
6 | text-align: center;
7 | `;
8 |
--------------------------------------------------------------------------------
/src/lib/hooks/index.js:
--------------------------------------------------------------------------------
1 | import useShex from './useShex';
2 | import { useNotification } from './useNotification';
3 |
4 | export { useShex, useNotification }; // eslint-disable-line import/prefer-default-export
5 |
--------------------------------------------------------------------------------
/src/lib/context/shex.provider.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const ShexConfig = React.createContext({
4 | theme: {},
5 | languageTheme: {},
6 | config: {}
7 | });
8 |
9 | export default ShexConfig;
10 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/DateTimePicker/date-time.styles.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const ErrorMessage = styled.p`
4 | color: red;
5 | font-size: 0.8rem;
6 | margin: 5px 0 10px;
7 | `;
8 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/ErrorMessage/error-message.styled.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const ErrorWrapper = styled.p`
4 | color: red;
5 | font-size: 1.1rem;
6 | margin-top: 0;
7 | `;
8 |
--------------------------------------------------------------------------------
/src/lib/context/formModel.provider.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const FormModelConfig = React.createContext({
4 | theme: {},
5 | languageTheme: {
6 | save: 'Save'
7 | },
8 | config: {}
9 | });
10 |
11 | export default FormModelConfig;
12 |
--------------------------------------------------------------------------------
/src/demo/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import registerServiceWorker from './registerServiceWorker';
5 |
6 | ReactDOM.render( , document.getElementById('root'));
7 | registerServiceWorker();
8 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Classifier/classifier.style.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const SelectWrapper = styled.div`
4 | box-sizing: border-box;
5 | padding: 0.5em 1em;
6 | display: flex;
7 | align-items: center;
8 | `;
9 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/ErrorMessage/error-message.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ErrorWrapper } from './error-message.styled';
3 |
4 | export const ErrorMessage = ({ valid, errorMessage }) =>
5 | !valid && {errorMessage} ;
6 |
--------------------------------------------------------------------------------
/src/lib/context/index.js:
--------------------------------------------------------------------------------
1 | import ShexConfig from './shex.provider';
2 | import FormModelConfig from './formModel.provider';
3 | import { ThemeContext } from './ThemeContext';
4 |
5 | // eslint-disable-next-line import/prefer-default-export
6 | export { ShexConfig, FormModelConfig, ThemeContext };
7 |
--------------------------------------------------------------------------------
/.storybook/addons.js:
--------------------------------------------------------------------------------
1 | import '@storybook/addons';
2 | import '@storybook/addon-console';
3 | import 'storybook-readme/register';
4 | import '@storybook/addon-knobs/register';
5 | import '@storybook/addon-actions/register';
6 | import '@storybook/addon-jest/register';
7 | import 'storybook-addon-jsx/register';
8 |
--------------------------------------------------------------------------------
/src/lib/classes/index.js:
--------------------------------------------------------------------------------
1 | import { Notification } from './notification';
2 | import AccessControlList from './access-control-list';
3 | import ACLFactory from './access-control-factory';
4 | import AppPermission from './app-permissions';
5 |
6 | export { Notification, AccessControlList, ACLFactory, AppPermission };
7 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to the Solid React Components Library
2 |
3 | Thank you for your interest in contributing to the Solid React Components Library!
4 |
5 | Before getting started, please review the [Solid React SDK Contributor Guide](https://github.com/Inrupt-inc/solid-react-sdk/blob/master/CONTRIBUTING.md).
6 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Heading/heading.component.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 |
3 | import { UI } from '@inrupt/lit-generated-vocab-common';
4 |
5 | export const Heading = props => {
6 | const { data } = props;
7 |
8 | return {data[UI.contents]} ;
9 | };
10 |
--------------------------------------------------------------------------------
/src/lib/utils/error.js:
--------------------------------------------------------------------------------
1 | export default class SolidError extends Error {
2 | constructor(message, name, code) {
3 | super(message);
4 | this.message = message;
5 | this.statusText = message;
6 | this.name = name || 'SolidError';
7 | this.type = this.name;
8 | this.code = code || 0;
9 | this.status = code || 0;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | // This is a custom Jest transformer turning file imports into filenames.
4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
5 |
6 | module.exports = {
7 | process(src, filename) {
8 | return `module.exports = ${JSON.stringify(path.basename(filename))};`;
9 | }
10 | };
11 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | // This is a custom Jest transformer turning style imports into empty objects.
2 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
3 |
4 | module.exports = {
5 | process() {
6 | return 'module.exports = {};';
7 | },
8 | getCacheKey() {
9 | // The output is always the same.
10 | return 'cssTransform';
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/src/lib/components/ShexFormBuilder/children/ShexFormLive/styled.component.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/prefer-default-export */
2 | import styled from 'styled-components';
3 |
4 | export const FormComponent = styled.form`
5 | button {
6 | margin: 20px 10px;
7 | border: 1px solid hsl(0, 0%, 80%);
8 | cursor: pointer;
9 | padding: 10px 30px;
10 | }
11 | `;
12 |
--------------------------------------------------------------------------------
/stories/LogoutButton/README.md:
--------------------------------------------------------------------------------
1 | ### LogoutButton
2 |
3 | This component uses solid-auth-client to provide a simple button that logs out the user. It is a simple helper component to integrate with solid-auth-client.
4 |
5 | We re-expose this component from [@solid/react](https://github.com/solid/react-components) library.
6 |
7 | ```javascript
8 |
9 | ```
10 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/DateLine/date-line.style.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Wrapper = styled.div`
4 | //display: flex;
5 | padding: 8px 0;
6 | flex-direction: column;
7 | `;
8 |
9 | export const Label = styled.span`
10 | font-size: 16px;
11 | letter-spacing: 0.4px;
12 | `;
13 |
14 | export const Value = styled.span``;
15 |
--------------------------------------------------------------------------------
/stories/withAuthorization/README.md:
--------------------------------------------------------------------------------
1 | ### withAuthorization
2 |
3 | This component is a wrapper for withWebId. It provides additional functionality, such as discovering when a user is not authenticated and redirecting them to a custom route, or to /login, when the user is not authenticated.
4 |
5 | ```javascript
6 | export default withAuthorization(WelcomeComponent, );
7 | ```
8 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/SingleLine/single-line.style.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Wrapper = styled.div`
4 | display: flex;
5 | padding: 8px 0;
6 | flex-direction: column;
7 | `;
8 |
9 | export const Label = styled.span`
10 | font-size: 16px;
11 | letter-spacing: 0.4px;
12 | `;
13 |
14 | export const Value = styled.span``;
15 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Input/input.styles.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const InputGroup = styled.div`
4 | box-sizing: border-box;
5 | padding: 0.5em 1em;
6 | display: flex;
7 | align-items: center;
8 | & input {
9 | border-radius: 2px;
10 | border: solid 1px #ccc;
11 | padding: 0.5em;
12 | margin-left: 1em;
13 | }
14 | `;
15 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/index.js:
--------------------------------------------------------------------------------
1 | import ExpressionFields from './expression-fields.component';
2 | import AddButton from './AddButton';
3 | import DeleteButton from './DeleteButton';
4 | import Field from './Field';
5 | import InputField from './InputField';
6 | import DropDownField from './DropDownField';
7 |
8 | export { ExpressionFields, AddButton, DeleteButton, Field, InputField, DropDownField };
9 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/RadioButtonList/radio-button-list.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import RadioButton from '../RadioButton';
3 |
4 | const RadioButtonList = ({ list, name }) => {
5 | return (
6 |
7 | {list.map(check => (
8 |
9 | ))}
10 |
11 | );
12 | };
13 |
14 | export default RadioButtonList;
15 |
--------------------------------------------------------------------------------
/stories/Image/image.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@storybook/react';
3 | import { Image } from '@solid-react-components';
4 | import Readme from './README.md';
5 |
6 | storiesOf('Image', module)
7 | .addParameters({
8 | readme: {
9 | // Show readme at the addons panel
10 | sidebar: Readme
11 | }
12 | })
13 | .add('default', () => );
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # production
7 | /build
8 | /demo
9 |
10 | # testing
11 | /coverage
12 |
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | # editors
22 | .idea
23 | *.iml
24 |
25 | npm-debug.log*
26 | yarn-debug.log*
27 | yarn-error.log*
28 |
--------------------------------------------------------------------------------
/src/lib/components/ProviderSelect/provider.select.component.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { ProviderSelect } from '@components';
4 |
5 | afterAll(cleanup);
6 |
7 | describe('ProviderSelect', () => {
8 | const { container } = render( );
9 | it('should render without crashing', () => {
10 | expect(container).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/DropDownField/styled.component.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const ErrorMessage = styled.p`
4 | margin: 0;
5 | color: red;
6 | `;
7 |
8 | export const SelectWrapper = styled.div`
9 | position: relative;
10 | &.error {
11 | margin-bottom: 0;
12 | }
13 | `;
14 |
15 | export const Select = styled.select`
16 | .error & {
17 | margin-bottom: 5px;
18 | }
19 | `;
20 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/MultiLine/multi-line.style.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Wrapper = styled.div`
4 | display: flex;
5 | padding: 8px 0;
6 | flex-direction: column;
7 | `;
8 |
9 | export const Label = styled.label`
10 | font-weight: bold;
11 | font-size: 16px;
12 | letter-spacing: 0.4px;
13 | `;
14 |
15 | export const Value = styled.p`
16 | white-space: pre-wrap;
17 | `;
18 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/CheckBoxList/check-box-list.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CheckBox from '../CheckBox/check-box.component';
3 |
4 | type Props = {
5 | list: any[]
6 | };
7 |
8 | const CheckBoxList = ({ list }: Props) => {
9 | return (
10 |
11 | {list.map(check => (
12 |
13 | ))}
14 |
15 | );
16 | };
17 |
18 | export default CheckBoxList;
19 |
--------------------------------------------------------------------------------
/src/lib/components/ProfileUploader/profile-uploader.style.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const ProfileWrapper = styled.div`
4 | border-radius: 60px;
5 | text-align: center;
6 | width: 129px;
7 | height: 129px;
8 | `;
9 |
10 | export const ImgStyle = styled.img`
11 | border-radius: 60px;
12 | width: 100%;
13 | height: 100%;
14 | `;
15 |
16 | export const ButtonStyle = styled.button`
17 | margin-top: 10px;
18 | `;
19 |
--------------------------------------------------------------------------------
/stories/LogoutButton/logout-button.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@storybook/react';
3 | import { LogoutButton } from '@solid-react-components';
4 |
5 | import LogoutButtonReadme from './README.md';
6 |
7 | storiesOf('Logout button', module)
8 | .addParameters({
9 | readme: {
10 | // Show readme at the addons panel
11 | sidebar: LogoutButtonReadme
12 | }
13 | })
14 | .add('default', () => );
15 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Select/select.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import Select from './select.component';
4 | import 'jest-dom/extend-expect';
5 |
6 | afterAll(cleanup);
7 |
8 | describe('Provider Login Container', () => {
9 | const { container } = render( );
10 |
11 | it('shoud renders without crashing', () => {
12 | expect(container).toBeTruthy();
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/DeleteButton/delete-button.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | type Props = {
4 | action: id => void,
5 | id: string,
6 | text: string,
7 | className: string
8 | };
9 |
10 | export const DeleteButton = ({ action, text = 'Delete', type, id, className }: Props) =>
11 | type.includes('Group') && (
12 | action(id)} className={className}>
13 | {text}
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/TextArea/text-area.styles.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const TextAreaGroup = styled.div`
4 | box-sizing: border-box;
5 | padding: 0.5em 1em;
6 | display: flex;
7 | align-items: center;
8 | width: 100%;
9 | & label {
10 | width: 100%;
11 | }
12 | & textarea {
13 | border-radius: 4px;
14 | border: solid 1px #ccc;
15 | padding: 0.5em;
16 | font-size: 0.8em;
17 | height: 100px;
18 | }
19 | `;
20 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/CheckBoxList/check-box-list.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import CheckBoxList from './check-box-list.component';
4 | import 'jest-dom/extend-expect';
5 |
6 | afterAll(cleanup);
7 |
8 | describe('Provider Login Container', () => {
9 | const { container } = render(
);
10 |
11 | it('should render without crashing', () => {
12 | expect(container).toBeTruthy();
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/RadioButtonList/radio-button-list.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import RadioButtonList from './radio-button-list.component';
4 | import 'jest-dom/extend-expect';
5 |
6 | afterAll(cleanup);
7 |
8 | describe('Provider Login Container', () => {
9 | const { container } = render(
);
10 |
11 | it('shoud renders without crashing', () => {
12 | expect(container).toBeTruthy();
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/src/test/__mocks__/@solid/query-ldflex.js:
--------------------------------------------------------------------------------
1 | const { context } = jest.requireActual('@solid/query-ldflex').default;
2 |
3 | const ldflex = {
4 | context,
5 | resolve: jest.fn(),
6 | clearCache: jest.fn(),
7 | delete: jest.fn(() => true),
8 | add: jest.fn(() => true),
9 | properties: [],
10 | subjects: []
11 | };
12 | ldflex['ldp:inbox'] = ldflex;
13 | ldflex['https://example.org/#me'] = ldflex['ldp:inbox'];
14 | ldflex['https://example.org/public/test.ttl.acl'] = { ...ldflex };
15 |
16 | export default ldflex;
17 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Comment/comment.component.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { UI } from '@inrupt/lit-generated-vocab-common';
3 | import { ThemeContext } from '@context';
4 |
5 | type Props = {
6 | data: object
7 | };
8 |
9 | export const Comment = (props: Props) => {
10 | const { data } = props;
11 | const { [UI.contents]: comment } = data;
12 |
13 | const { theme } = useContext(ThemeContext);
14 |
15 | return {comment}
;
16 | };
17 |
--------------------------------------------------------------------------------
/stories/ProfileUploader/profile-uploader.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@storybook/react';
3 | import { ProfileUploader } from '@solid-react-components';
4 | import ProfileUploaderReadme from './README.md';
5 |
6 | storiesOf('Profile Uploader', module)
7 | .addParameters({
8 | readme: {
9 | // Show readme at the addons panel
10 | sidebar: ProfileUploaderReadme
11 | },
12 | jest: ['profile-uploader.test.js']
13 | })
14 | .add('default', () => );
15 |
--------------------------------------------------------------------------------
/stories/withWebId/README.md:
--------------------------------------------------------------------------------
1 | ### withWebId
2 |
3 | In Solid, people are identified by a WebID, which is essentially a URL link that points to them and leads to their data.
4 |
5 | By wrapping your component definition with withWebId, the WebID property will automatically be set on your component's instances whenever the login status changes.
6 |
7 | We re-expose this component from [@solid/react](https://github.com/solid/react-components) library.
8 |
9 | ```javascript
10 | const MyComponent = withWebId(props => Hey user, your WebID is {props.webID}.
);
11 | ```
12 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Spinner/spinner.component.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useContext } from 'react';
2 |
3 | import { ThemeContext } from '@context';
4 |
5 | type Props = {
6 | errored: boolean,
7 | running: boolean
8 | };
9 |
10 | export const Spinner = (props: Props) => {
11 | const { errored, running } = props;
12 | const { theme } = useContext(ThemeContext);
13 |
14 | return (
15 |
16 | {running ? Saving... : null}
17 | {errored ? {Error} : null}
18 |
19 | );
20 | };
21 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/InputField/styled.component.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const ErrorMessage = styled.p`
4 | margin: 0;
5 | color: red;
6 | display: block;
7 | `;
8 |
9 | export const InputWrapper = styled.div`
10 | position: relative;
11 | &.error {
12 | margin-bottom: 0;
13 | }
14 | `;
15 |
16 | export const Input = styled.input`
17 | .error & {
18 | margin-bottom: 5px;
19 | }
20 |
21 | &:disabled {
22 | background: #c0c0c0;
23 | }
24 | `;
25 |
26 | export const InputGroup = styled.div`
27 | display: flex;
28 | `;
29 |
--------------------------------------------------------------------------------
/stories/withWebId/with-webid.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@storybook/react';
3 | import { withWebId } from '@solid-react-components';
4 | import withWebIdReadme from './README.md';
5 |
6 | const Component = props => (
7 |
8 | Props: {JSON.stringify(props)}
9 |
10 | );
11 |
12 | const ComponentWebId = withWebId(Component);
13 |
14 | storiesOf('withWebId', module)
15 | .addParameters({
16 | readme: {
17 | // Show readme at the addons panel
18 | content: withWebIdReadme
19 | }
20 | })
21 | .add('default', () => );
22 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/viewer.style.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Group = styled.div`
4 | padding: ${({ parent }) => (parent ? '0' : '1em')};
5 | ${({ parent }) =>
6 | !parent
7 | ? `
8 |
9 | // display: grid;
10 | // grid-template-columns: 1fr 1fr;
11 | // grid-template-rows: auto;
12 | display: flex;
13 | flex-direction: column;
14 | `
15 | : `
16 | display: flex;
17 | flex-direction: column;`}
18 | `;
19 |
20 | export const Label = styled.span`
21 | font-weight: normal;
22 | font-size: 18px;
23 | letter-spacing: 0.5px;
24 | padding: 4px;
25 | `;
26 |
--------------------------------------------------------------------------------
/src/lib/constants/index.js:
--------------------------------------------------------------------------------
1 | import { UI } from '@inrupt/lit-generated-vocab-common';
2 |
3 | export const PERMISSIONS = {
4 | APPEND: 'Append',
5 | READ: 'Read',
6 | WRITE: 'Write',
7 | CONTROL: 'Control'
8 | };
9 |
10 | export const InputTextTypes = {
11 | [UI.SingleLineTextField]: 'text',
12 | [UI.EmailField]: 'email',
13 | [UI.PhoneField]: 'phone',
14 | [UI.DecimalField]: 'number',
15 | [UI.FloatField]: 'number',
16 | [UI.IntegerField]: 'number'
17 | };
18 |
19 | /* See for format explanations */
20 | export const DATE_FORMAT = {
21 | DATE: 'yyyy-MM-dd',
22 | TIME: 'kk:mm:ss'
23 | };
24 |
--------------------------------------------------------------------------------
/src/lib/components/ProviderSelect/styled.components.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Item = styled.div`
4 | cursor: pointer;
5 | display: block;
6 | padding: 10px 5px;
7 | border-bottom: 1px solid rgba(0, 0, 0, 0.1);
8 |
9 | &:last-child {
10 | border: none;
11 | }
12 |
13 | &:hover {
14 | background-color: #ebf5ff;
15 | }
16 | `;
17 |
18 | export const Icon = styled.img`
19 | width: 32px;
20 | margin-right: 10px;
21 | display: inline-block;
22 | vertical-align: middle;
23 | `;
24 |
25 | export const ItemText = styled.span`
26 | display: inline-block;
27 | vertical-align: middle;
28 | `;
29 |
--------------------------------------------------------------------------------
/stories/ShexForm/shex-form.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@storybook/react';
3 | import { withKnobs, text, object } from '@storybook/addon-knobs';
4 | import { ShexForm } from '@solid-react-components';
5 | import ProviderLoginReadme from './README.md';
6 | import shexj from '../../src/assets/shexj.json';
7 |
8 | storiesOf('Shex ShapeForm', module)
9 | .addDecorator(withKnobs)
10 | .addParameters({
11 | readme: {
12 | // Show readme at the addons panel
13 | sidebar: ProviderLoginReadme
14 | },
15 | jest: ['shex-form.test.js']
16 | })
17 | .add('default', () => );
18 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/Field/field.component.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { cleanup, render } from '@testing-library/react';
3 | import Field from './field.component';
4 | import 'jest-dom/extend-expect';
5 |
6 | afterAll(cleanup);
7 |
8 | const setup = props => {
9 | return ;
10 | };
11 |
12 | describe('Shex ShapeForm Component', () => {
13 | const component = setup({
14 | data: { valueExpr: {} },
15 | inputData: { name: 'test' }
16 | });
17 | const { container } = render(component);
18 |
19 | it('should renders without crashing', () => {
20 | expect(container).toBeTruthy();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/index.js:
--------------------------------------------------------------------------------
1 | import CheckBox from './CheckBox';
2 | import { DateTimePicker } from './DateTimePicker';
3 | import Input from './Input';
4 | import RadioButton from './RadioButton';
5 | import Select from './Select';
6 | import TextArea from './TextArea';
7 | import CheckBoxList from './CheckBoxList';
8 | import RadioButtonList from './RadioButtonList';
9 | import ColorPicker from './ColorPicker';
10 | import Email from './Email';
11 | import Phone from './Phone';
12 |
13 | export {
14 | CheckBox,
15 | CheckBoxList,
16 | DateTimePicker,
17 | Input,
18 | RadioButton,
19 | RadioButtonList,
20 | Select,
21 | TextArea,
22 | ColorPicker,
23 | Email,
24 | Phone
25 | };
26 |
--------------------------------------------------------------------------------
/stories/withAuthorization/with-authorization.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@storybook/react';
3 | import { withWebId } from '@solid-react-components';
4 | import withAuthorizationReadme from './README.md';
5 |
6 | const Component = props => (
7 |
8 | Component Props: {JSON.stringify(props)}
9 |
10 | );
11 |
12 | const ComponentWebId = withWebId(Component);
13 |
14 | storiesOf('withAuthorization', module)
15 | .addParameters({
16 | readme: {
17 | // Show readme at the addons panel
18 | content: withAuthorizationReadme
19 | },
20 | jest: ['with-authorization.test.js']
21 | })
22 | .add('default', () => );
23 |
--------------------------------------------------------------------------------
/src/lib/components/ProfileViewer/profile-viewer.style.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const ProfileCard = styled.div`
4 | border: solid 1px #ccc;
5 | border-radius: 8px;
6 | min-width: 200px;
7 | width: auto;
8 | min-height: 200px;
9 | height: auto;
10 | box-shadow: #ccc 2px 2px 8px;
11 | padding: 6px;
12 | position: absolute;
13 | z-index:1000;
14 | background-color: #fff;
15 |
16 | bottom: ${({ direction }) => (direction === 'up' ? '20px' : 'auto')}
17 | top: ${({ direction }) => (direction === 'down' ? '20px' : 'auto')}
18 |
19 | img {
20 | max-width: 100%;
21 | }
22 |
23 | `;
24 |
25 | export const ProfileViewerWrapper = styled.span`
26 | position: relative;
27 | `;
28 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/ColorLine/color-line.style.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Wrapper = styled.div`
4 | //display: flex;
5 | padding: 8px 0;
6 | flex-direction: column;
7 | `;
8 |
9 | export const Label = styled.span`
10 | font-weight: bold;
11 | font-size: 16px;
12 | letter-spacing: 0.4px;
13 | `;
14 |
15 | export const Value = styled.span`
16 | display: inline-flex;
17 | `;
18 |
19 | export const ColorSwatch = styled.div`
20 | width: 36px;
21 | height: 14px;
22 | padding: 5px;
23 | background: ${props => props.color};
24 | border-radius: 1px;
25 | box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
26 | display: inline-block;
27 | cursor: pointer;
28 | `;
29 |
--------------------------------------------------------------------------------
/src/lib/components/index.js:
--------------------------------------------------------------------------------
1 | import ProviderSelect from './ProviderSelect';
2 | import ProviderLogin from './ProviderLogin';
3 | import PrivateRoute from './PrivateRoute';
4 | import withAuthorization from './withAuthorization';
5 | import Uploader from './Uploader';
6 | import ProfileUploader from './ProfileUploader';
7 | import ShexForm from './ShexForm';
8 | import ShexFormBuilder from './ShexFormBuilder';
9 | import ProfileViewer from './ProfileViewer';
10 | import { FormModel, Spinner } from './FormModel';
11 |
12 | export {
13 | ProviderSelect,
14 | ProviderLogin,
15 | PrivateRoute,
16 | withAuthorization,
17 | Uploader,
18 | ProfileUploader,
19 | ShexForm,
20 | ShexFormBuilder,
21 | ProfileViewer,
22 | FormModel,
23 | Spinner
24 | };
25 |
--------------------------------------------------------------------------------
/.storybook/config.js:
--------------------------------------------------------------------------------
1 | import { configure, addDecorator } from '@storybook/react';
2 | import { addReadme } from 'storybook-readme';
3 | import { withTests } from '@storybook/addon-jest';
4 | import StoryRouter from 'storybook-react-router';
5 |
6 | import results from '../jest-test-results.json';
7 |
8 | import { jsxDecorator } from 'storybook-addon-jsx';
9 |
10 | addDecorator(jsxDecorator);
11 |
12 | addDecorator(
13 | withTests({
14 | results
15 | })
16 | );
17 | addDecorator(addReadme);
18 |
19 | addDecorator(StoryRouter());
20 |
21 | // automatically import all files ending in *.stories.js
22 | const req = require.context('../stories', true, /.stories.js$/);
23 | function loadStories() {
24 | req.keys().forEach(filename => req(filename));
25 | }
26 |
27 | configure(loadStories, module);
28 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Comment/comment.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText } from '@testing-library/dom';
4 | import { Comment } from './comment.component';
5 | import 'jest-dom/extend-expect';
6 | import { UI } from '@inrupt/lit-generated-vocab-common';
7 |
8 | afterAll(cleanup);
9 |
10 | test('Renders without crashing', () => {
11 | const data = {};
12 | const { container } = render( );
13 | expect(container).toBeTruthy();
14 | });
15 |
16 | test('Renders the comment', () => {
17 | const data = {
18 | [UI.contents]: 'comment'
19 | };
20 | const { container } = render( );
21 | expect(getByText(container, 'comment')).toBeTruthy();
22 | });
23 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/SingleLine/single-line.component.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { UI } from '@inrupt/lit-generated-vocab-common';
3 | import { ThemeContext } from '@context';
4 |
5 | type Props = {
6 | id: string,
7 | data: object
8 | };
9 |
10 | export const SingleLine = (props: Props) => {
11 | const { id, data } = props;
12 | const { theme } = useContext(ThemeContext);
13 |
14 | const { [UI.label]: label, [UI.value]: value } = data;
15 |
16 | return (
17 |
18 |
19 | {label}
20 |
21 |
22 | {value}
23 |
24 |
25 | );
26 | };
27 |
--------------------------------------------------------------------------------
/stories/PrivateRoute/private-route.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@storybook/react';
3 | import { withKnobs, text, object } from '@storybook/addon-knobs';
4 | import { PrivateRoute } from '@solid-react-components/components/PrivateRoute/private-route.component';
5 |
6 | import Readme from './README.md';
7 |
8 | const LoggedIn = () => (
9 |
10 | You're logged in
11 |
12 | );
13 | storiesOf('Private Route', module)
14 | .addParameters({
15 | readme: {
16 | // Show readme at the addons panel
17 | sidebar: Readme
18 | },
19 | jest: ['private-route.component.test.js']
20 | })
21 | .addDecorator(withKnobs)
22 | .add('WebId', () => );
23 |
--------------------------------------------------------------------------------
/src/lib/hooks/useShex.test.js:
--------------------------------------------------------------------------------
1 | import { renderHook } from '@testing-library/react-hooks';
2 | import { cleanup } from '@testing-library/react';
3 | import { act } from 'react-dom/test-utils';
4 | import { useShex } from '@hooks';
5 |
6 | const setup = () => {
7 | let component;
8 |
9 | act(() => {
10 | component = renderHook(() => useShex(null, null, null, { errorCallback() {} }));
11 | });
12 |
13 | return component;
14 | };
15 |
16 | describe('useShex', () => {
17 | afterAll(() => cleanup);
18 |
19 | it('returns object when shexC is not loaded', async () => {
20 | const { result } = setup();
21 |
22 | expect(result.current.shexData).toEqual({});
23 | expect(result.current.addNewShexField).toBeTruthy();
24 | expect(result.current.updateShexJ).toBeTruthy();
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Heading/heading.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText } from '@testing-library/dom';
4 | import { Heading } from './heading.component';
5 | import { UI } from '@inrupt/lit-generated-vocab-common';
6 |
7 | import 'jest-dom/extend-expect';
8 |
9 | afterAll(cleanup);
10 |
11 | test('Renders without crashing', () => {
12 | const data = {};
13 | const { container } = render( );
14 | expect(container).toBeTruthy();
15 | });
16 |
17 | test('Renders the heading', () => {
18 | const data = {
19 | [UI.contents]: 'heading'
20 | };
21 | const { container } = render( );
22 | expect(getByText(container, 'heading')).toBeTruthy();
23 | });
24 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Decimal/decimal.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText } from '@testing-library/dom';
4 | import { Decimal } from './decimal.component';
5 | import 'jest-dom/extend-expect';
6 | import { UI } from '@inrupt/lit-generated-vocab-common';
7 |
8 | afterAll(cleanup);
9 |
10 | test('Renders without crashing', () => {
11 | const data = {};
12 | const { container } = render( );
13 | expect(container).toBeTruthy();
14 | });
15 |
16 | test('Renders the decimal label', () => {
17 | const data = {
18 | [UI.label]: 'decimal label'
19 | };
20 | const { container } = render( );
21 | expect(getByText(container, 'decimal label')).toBeTruthy();
22 | });
23 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/ColorLine/color-line.component.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { UI } from '@inrupt/lit-generated-vocab-common';
3 | import { ThemeContext } from '@context';
4 |
5 | import { Wrapper, Label, Value, ColorSwatch } from './color-line.style';
6 |
7 | export const ColorLine = props => {
8 | const { id, data } = props;
9 | const { theme } = useContext(ThemeContext);
10 |
11 | const { [UI.label]: label, [UI.value]: value } = data;
12 |
13 | return (
14 |
15 | {label}
16 |
17 | {value}
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | export default ColorLine;
25 |
--------------------------------------------------------------------------------
/scripts/test.js:
--------------------------------------------------------------------------------
1 | // Do this as the first thing so that any code reading it knows the right env.
2 | process.env.BABEL_ENV = 'test';
3 | process.env.NODE_ENV = 'test';
4 | process.env.PUBLIC_URL = '';
5 |
6 | // Makes the script crash on unhandled rejections instead of silently
7 | // ignoring them. In the future, promise rejections that are not handled will
8 | // terminate the Node.js process with a non-zero exit code.
9 | process.on('unhandledRejection', err => {
10 | throw err;
11 | });
12 |
13 | // Ensure environment variables are read.
14 | require('../config/env');
15 |
16 | const jest = require('jest');
17 | const argv = process.argv.slice(2);
18 |
19 | // Watch unless on CI or in coverage mode
20 | if (!process.env.CI && argv.indexOf('--coverage') < 0) {
21 | argv.push('--watch');
22 | }
23 |
24 | jest.run(argv);
25 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/ColorLine/color-line.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import '@testing-library/jest-dom/extend-expect';
3 | import { render, screen } from '@testing-library/react';
4 |
5 | import { ColorLine } from './color-line.component';
6 | import { UI } from '@inrupt/lit-generated-vocab-common';
7 |
8 | test('Renders with value and label', async () => {
9 | const data = { [UI.value]: '82346-123438-asdf8324', [UI.label]: 'Set content' };
10 | render( );
11 |
12 | expect(screen.getByText('Set content')).toBeInTheDocument();
13 | });
14 |
15 | test('Renders without label', async () => {
16 | const data = { [UI.value]: '234-542340-sdf58923' };
17 | const { container } = render( );
18 |
19 | expect(container.firstChild.firstChild).toBeEmpty();
20 | });
21 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/AddButton/add-button.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shexUtil } from '@utils';
3 | import { ShexConfig } from '@context';
4 |
5 | const AddButton = ({ expression, defaultExpression, allowNewFields, text = '+ Add new' }) => {
6 | return (allowNewFields && (
7 |
8 | {({ theme, languageTheme: { language, addButtonText }, config: { addNewShexField } }) => (
9 | addNewShexField(defaultExpression, expression)}
11 | type="button"
12 | className={theme && theme.addButtonStyle}
13 | >
14 | {`${addButtonText || text} ${shexUtil.formLabel(defaultExpression, language)}`}
15 |
16 | )}
17 |
18 | ): null);
19 | };
20 |
21 | export default AddButton;
22 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/BoolLine/bool-line.component.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { UI } from '@inrupt/lit-generated-vocab-common';
3 | import { ThemeContext } from '@context';
4 |
5 | type Props = {
6 | id: string,
7 | data: object
8 | };
9 |
10 | export const BoolLine = (props: Props) => {
11 | const { id, data } = props;
12 | const { theme } = useContext(ThemeContext);
13 |
14 | const { [UI.value]: value, [UI.label]: label } = data;
15 |
16 | return (
17 |
18 | {label}
19 |
28 |
29 | );
30 | };
31 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/ColorPicker/color-picker.styles.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const PickerGroup = styled.div`
4 | box-sizing: border-box;
5 | padding: 0.5em 1em;
6 | display: flex;
7 | align-items: center;
8 | & label {
9 | padding: 0.5em;
10 | margin-right: 1em;
11 | }
12 | `;
13 |
14 | export const ColorSwatch = styled.div`
15 | width: 36px;
16 | height: 14px;
17 | padding: 5px;
18 | background: ${props => props.color};
19 | border-radius: 1px;
20 | box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
21 | display: inline-block;
22 | cursor: pointer;
23 | `;
24 |
25 | export const Cover = styled.div`
26 | position: fixed;
27 | top: 0;
28 | right: 0;
29 | bottom: 0;
30 | left: 0;
31 | `;
32 |
33 | export const Popover = styled.div`
34 | position: absolute;
35 | z-index: 2;
36 | `;
37 |
--------------------------------------------------------------------------------
/src/lib/styled-components/form.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const SolidInput = styled.input`
4 | background-color: hsl(0, 0%, 100%);
5 | border-color: hsl(0, 0%, 80%);
6 | border-radius: 4px;
7 | border-style: solid;
8 | border-width: 1px;
9 | box-sizing: border-box;
10 | width: 100%;
11 | padding: 2px 8px;
12 | height: 30px;
13 | `;
14 |
15 | export const SolidLinkButton = styled.button`
16 | border: none;
17 | background: transparent;
18 | cursor: pointer;
19 |
20 | &:hover {
21 | text-decoration: underline;
22 | }
23 | `;
24 |
25 | export const SolidButton = styled.button`
26 | border: 1px solid hsl(0, 0%, 80%);
27 | cursor: pointer;
28 | padding: 10px 30px;
29 |
30 | &:hover {
31 | opacity: 0.7;
32 | }
33 | `;
34 |
35 | export const ErrorMessage = styled.p`
36 | color: red;
37 | margin: 0;
38 | `;
39 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/DeleteButton/delete-button.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText } from '@testing-library/dom';
4 | import { DeleteButton } from './delete-button.component';
5 | import 'jest-dom/extend-expect';
6 |
7 | afterAll(cleanup);
8 |
9 | test('Renders without crashing', () => {
10 | const props = {
11 | type: 'Group'
12 | };
13 | const { container } = render( );
14 | expect(getByText(container, 'Delete')).toBeTruthy();
15 | expect(container).toBeTruthy();
16 | });
17 |
18 | test('Renders the text', () => {
19 | const props = {
20 | type: 'Group',
21 | text: 'Button Text'
22 | };
23 | const { container } = render( );
24 | expect(getByText(container, 'Button Text')).toBeTruthy();
25 | });
26 |
--------------------------------------------------------------------------------
/config/polyfills.js:
--------------------------------------------------------------------------------
1 | if (typeof Promise === 'undefined') {
2 | // Rejection tracking prevents a common issue where React gets into an
3 | // inconsistent state due to an error, but it gets swallowed by a Promise,
4 | // and the user has no idea what causes React's erratic future behavior.
5 | require('promise/lib/rejection-tracking').enable();
6 | window.Promise = require('promise/lib/es6-extensions.js');
7 | }
8 |
9 | // fetch() polyfill for making API calls.
10 | require('whatwg-fetch');
11 |
12 | // Object.assign() is commonly used with React.
13 | // It will use the native implementation if it's present and isn't buggy.
14 | Object.assign = require('object-assign');
15 |
16 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet.
17 | // We don't polyfill it in the browser--this is user's responsibility.
18 | if (process.env.NODE_ENV === 'test') {
19 | require('raf').polyfill(global);
20 | }
21 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/MultiLine/multi-line.component.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { UI } from '@inrupt/lit-generated-vocab-common';
3 | import { ThemeContext } from '@context';
4 | import { Wrapper, Label, Value } from './multi-line.style';
5 |
6 | type Props = {
7 | id: string,
8 | data: object
9 | };
10 |
11 | export const MultiLine = (props: Props) => {
12 | const { id, data } = props;
13 | const { theme } = useContext(ThemeContext);
14 | const { [UI.label]: label, [UI.value]: value } = data;
15 |
16 | return (
17 |
18 |
19 |
20 | {label}
21 |
22 |
23 | {value || ''}
24 |
25 |
26 |
27 | );
28 | };
29 |
30 | export default MultiLine;
31 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/styled.component.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Panel = styled.div`
4 | border: solid 1px red;
5 | padding: 10px;
6 | position: relative;
7 | margin: 10px 0;
8 |
9 | ul {
10 | padding: 0;
11 | margin: 0;
12 | }
13 |
14 | li {
15 | list-style: none;
16 | }
17 |
18 | select {
19 | display: block;
20 | margin: 20px 0;
21 | padding: 10px;
22 | }
23 |
24 | label {
25 | display: block;
26 | margin-top: 15px;
27 | }
28 |
29 | button {
30 | margin: 20px 0;
31 | border: 1px solid hsl(0, 0%, 80%);
32 | cursor: pointer;
33 | padding: 10px 30px;
34 | top: 0;
35 | }
36 | `;
37 |
38 | export const DeleteButton = styled.button`
39 | display: inline-flex;
40 | position: absolute;
41 | right: 8px;
42 | top: 5px;
43 | color: red;
44 | border: none;
45 | background: none;
46 | cursor: pointer;
47 | z-index: 100;
48 | `;
49 |
--------------------------------------------------------------------------------
/src/lib/components/ShexFormBuilder/shex-form-builder.component.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { cleanup, render } from '@testing-library/react';
3 | import ShexFormBuilder from './shex-form-builder.component';
4 | import { act } from 'react-dom/test-utils';
5 | import 'jest-dom/extend-expect';
6 |
7 | afterAll(cleanup);
8 |
9 | const setup = props => {
10 | return ;
11 | };
12 |
13 | const defaultProps = {
14 | documentUri: '',
15 | shexUri: 'https://jpablo.solid.community/public/shapes/profile.shex',
16 | successCallback: null,
17 | errorCallback: null
18 | };
19 | describe('Shex ShapeForm Component', () => {
20 | let container;
21 | act(() => {
22 | const component = setup(defaultProps);
23 | const options = render(component);
24 | container = options.container;
25 | });
26 |
27 | it('should render without crashing', () => {
28 | expect(container).toBeTruthy();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/src/lib/components/withAuthorization/with-authorization.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { withWebId } from '@solid/react';
3 |
4 | export const withAuthorization = (Component, Loader) =>
5 | withWebId(
6 | class WithAuthorization extends React.Component {
7 | render() {
8 | const { webId } = this.props;
9 | switch (webId) {
10 | case undefined:
11 | return Loader || null;
12 | case null:
13 | // Using the non-SPA redirect here to clear the state when the user is not logged in
14 | // This helps with making sure state is fully clean on login, and addresses an issue with
15 | // the react-router-dom v5 upgrade, which didn't like using here
16 | window.location.href = '/login';
17 | return null;
18 | default:
19 | return ;
20 | }
21 | }
22 | }
23 | );
24 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/live-form-model.component.js:
--------------------------------------------------------------------------------
1 | import React, { memo } from 'react';
2 | import { LiveUpdate } from '@solid/react';
3 | import FormModel from './form-model.component';
4 |
5 | type Props = {
6 | modelPath: String,
7 | podPath: String,
8 | title: String,
9 | autoSave: boolean,
10 | onInit: () => void,
11 | onLoaded: () => void,
12 | onError: () => void,
13 | onSuccess: () => void,
14 | onSave: () => void,
15 | onAddNewField: () => void,
16 | onDelete: () => void,
17 | settings: {
18 | theme: object,
19 | languageTheme: object,
20 | config: object
21 | }
22 | };
23 |
24 | const LiveFormModel = memo(({ liveUpdate = false, podPath, ...props }: Props) => {
25 | return liveUpdate ? (
26 |
27 |
28 |
29 | ) : (
30 |
31 | );
32 | });
33 |
34 | export default LiveFormModel;
35 |
--------------------------------------------------------------------------------
/stories/PrivateRoute/README.md:
--------------------------------------------------------------------------------
1 | ### PrivateRoute
2 |
3 | Protected routes are an important part of any web application. Here we provide a custom component that can be used to check Solid authentication for you.
4 |
5 | The component will check to see if the user is logged in, using the withWebId component from the [@solid/react library](https://github.com/solid/react-components). If the user is not authenticated, they will be redirected to a route of your choosing, passed in via the props. If none is provided it will redirect to a /login route.
6 |
7 | ```javascript
8 |
9 | ```
10 |
11 | | Props | Type | Default | Description |
12 | | --------- | ------ | ------- | -------------------------------------------------- |
13 | | component | Node | null | Component to render after check if user is logged. |
14 | | redirect | String | /login | Redirect to login if user is not logged. |
15 |
--------------------------------------------------------------------------------
/src/lib/classes/access-control-factory.js:
--------------------------------------------------------------------------------
1 | import solid from 'solid-auth-client';
2 | import * as parse from 'parse-link-header';
3 | import AccessControlList from './access-control-list';
4 |
5 | /**
6 | * This factory will create and return a new ACL object while also fetching the Link
7 | * Header and returning the acl file location
8 | */
9 | export default class ACLFactory {
10 | static createNewAcl = async (owner, documentUri) => {
11 | const aclUrl = await this.getAclUriFromHeader(documentUri);
12 | const aclUrlValidated = new URL(aclUrl, documentUri).href;
13 | return new AccessControlList(owner, documentUri, aclUrlValidated);
14 | };
15 |
16 | static getAclUriFromHeader = async documentUri => {
17 | try {
18 | const response = await solid.fetch(documentUri, { method: 'HEAD' });
19 | const parsedLinks = parse(response.headers.get('Link'));
20 | return parsedLinks.acl ? parsedLinks.acl.url : '';
21 | } catch (error) {
22 | throw error;
23 | }
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": ["react-app", "airbnb", "prettier"],
4 | "plugins": ["react", "prettier"],
5 | "rules": {
6 | "prettier/prettier": [1],
7 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
8 | "no-underscore-dangle": [0],
9 | "import/no-unresolved": [0],
10 | "no-unused-vars": [1],
11 | "react/prop-types": [1],
12 | "no-shadow": [0],
13 | "no-restricted-syntax": [0],
14 | "consistent-return": [0],
15 | "import/prefer-default-export": [0],
16 | "import/no-cycle": [0],
17 | "quotes": [1, "single"],
18 | "react/no-array-index-key": [1],
19 | "react/jsx-one-expression-per-line": [0],
20 | "jsx-a11y/label-has-for": [0]
21 | },
22 | "settings": {
23 | "import/resolver": { "babel-module": {} }
24 | },
25 | "parserOptions": {
26 | "ecmaFeatures": {
27 | "jsx": true,
28 | "modules": true
29 | }
30 | },
31 | "env": {
32 | "browser": true,
33 | "jest": true
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/lib/utils/index.js:
--------------------------------------------------------------------------------
1 | import SolidError from './error';
2 | import * as shexUtil from './shex';
3 | import solidResponse from './statusMessage';
4 | import ShexFormValidator from './shexFormValidator';
5 | import {
6 | parseInitialValue,
7 | getLocale,
8 | isValidDate,
9 | getFormattedLocale,
10 | getClosestLocale
11 | } from './datetimes';
12 |
13 | import {
14 | fetchSchema,
15 | existDocument,
16 | createDocument,
17 | fetchLdflexDocument,
18 | getBasicPod,
19 | getIdpFromWebId
20 | } from './solidFetch';
21 |
22 | const getFileName = path => {
23 | // eslint-disable-next-line no-useless-escape
24 | return path.replace(/^.*[\\\/]/, '');
25 | };
26 |
27 | export {
28 | shexUtil,
29 | SolidError,
30 | solidResponse,
31 | fetchSchema,
32 | existDocument,
33 | createDocument,
34 | fetchLdflexDocument,
35 | ShexFormValidator,
36 | getFileName,
37 | getBasicPod,
38 | getLocale,
39 | parseInitialValue,
40 | isValidDate,
41 | getFormattedLocale,
42 | getClosestLocale,
43 | getIdpFromWebId
44 | };
45 |
--------------------------------------------------------------------------------
/src/test/__mocks__/solid-auth-client.js:
--------------------------------------------------------------------------------
1 | import EventEmitter from 'events';
2 | // import jest from 'jest';
3 |
4 | class SolidAuthClient extends EventEmitter {
5 | constructor() {
6 | super();
7 | this.session = undefined;
8 | }
9 |
10 | login() {}
11 |
12 | logout() {}
13 |
14 | trackSession(callback) {
15 | if (this.session !== undefined) callback(this.session);
16 | this.on('session', callback);
17 | }
18 | mockWebId(webId) {
19 | this.session = webId ? { webId } : null;
20 | this.emit('session', this.session);
21 | return new Promise(resolve => setImmediate(resolve));
22 | }
23 | currentSession() {}
24 |
25 | fetch() {
26 | return { ok: true, status: 200, url: 'https://example.org/public/test.ttl.acl' };
27 | }
28 | }
29 |
30 | const instance = new SolidAuthClient();
31 | jest.spyOn(instance, 'login');
32 | jest.spyOn(instance, 'logout');
33 | jest.spyOn(instance, 'trackSession');
34 | jest.spyOn(instance, 'removeListener');
35 | jest.spyOn(instance, 'fetch');
36 |
37 | export default instance;
38 |
--------------------------------------------------------------------------------
/src/lib/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | withWebId,
3 | LogoutButton,
4 | Image,
5 | LiveUpdate,
6 | useWebId,
7 | UpdateContext,
8 | useLiveUpdate
9 | } from '@solid/react';
10 | import {
11 | ProviderLogin,
12 | PrivateRoute,
13 | withAuthorization,
14 | Uploader,
15 | ProfileUploader,
16 | ShexForm,
17 | ShexFormBuilder,
18 | FormModel,
19 | ProfileViewer,
20 | Spinner
21 | } from '@components';
22 | import { shexUtil } from '@utils';
23 |
24 | import { useNotification } from '@hooks';
25 |
26 | import { AccessControlList, ACLFactory, AppPermission } from '@classes';
27 |
28 | export {
29 | ProviderLogin,
30 | PrivateRoute,
31 | withAuthorization,
32 | Uploader,
33 | ProfileUploader,
34 | withWebId,
35 | LogoutButton,
36 | Image,
37 | LiveUpdate,
38 | useWebId,
39 | UpdateContext,
40 | useLiveUpdate,
41 | ShexForm,
42 | ShexFormBuilder,
43 | useNotification,
44 | AccessControlList,
45 | AppPermission,
46 | FormModel,
47 | Spinner,
48 | ProfileViewer,
49 | ACLFactory,
50 | shexUtil
51 | };
52 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/InputField/input-field.component.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ShexConfig } from '@context';
3 | import { cleanup, render } from '@testing-library/react';
4 | import InputField from './input-field.component';
5 | import 'jest-dom/extend-expect';
6 |
7 | afterAll(cleanup);
8 |
9 | const config = { theme: {}, languageTheme: {}, config: {} };
10 |
11 | const setup = props => {
12 | return (
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | describe('Shex ShapeForm Component', () => {
20 | const component = setup({});
21 | const { container, rerender } = render(component);
22 |
23 | it('should renders without crashing', () => {
24 | expect(container).toBeTruthy();
25 | });
26 |
27 | it('should renders error when come from props', () => {
28 | const component = setup({ inputData: { error: 'Error' } });
29 |
30 | rerender(component);
31 | expect(container).toHaveTextContent('Error');
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/stories/Uploader/uploader.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@storybook/react';
3 | import { action } from '@storybook/addon-actions';
4 | import { Uploader, ProfileUploader } from '@solid-react-components';
5 | import UploaderReadme from './README.md';
6 |
7 | storiesOf('Uploader', module)
8 | .addParameters({
9 | readme: {
10 | // Show readme at the addons panel
11 | sidebar: UploaderReadme
12 | },
13 | jest: ['uploader.test.js']
14 | })
15 | .add('default', () => (
16 | ,
19 | limitFiles: 1,
20 | limitSize: 2100000,
21 | accept: 'png,jpg,jpeg',
22 | errorsText: {
23 | sizeLimit: 'Size limit',
24 | unsupported: 'Unsupported',
25 | maximumFiles: 'Max files'
26 | },
27 | onError: action('onError'),
28 | onComplete: action('onComplete'),
29 | onDrop: action('onDrop'),
30 | onStart: action('onStart')
31 | }}
32 | />
33 | ));
34 |
--------------------------------------------------------------------------------
/src/lib/components/PrivateRoute/private-route.component.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { withWebId } from '@solid/react';
3 | import { Redirect, Route } from 'react-router-dom';
4 |
5 | import { Loader } from './private-route.style';
6 |
7 | type Props = {
8 | webId: String | null,
9 | redirect?: String,
10 | component: Node,
11 | loaderComponent?: Node
12 | };
13 |
14 | export class PrivateRoute extends Component {
15 | renderRouter = (): React.Element => {
16 | const { webId, redirect, component: RenderComponent, ...rest } = this.props;
17 | return webId ? : ;
18 | };
19 |
20 | render() {
21 | const { webId, loaderComponent } = this.props;
22 | return webId === null || webId === undefined ? loaderComponent() : this.renderRouter();
23 | }
24 | }
25 |
26 | PrivateRoute.defaultProps = {
27 | redirect: '/login',
28 | loaderComponent: () => We are validating your data...
29 | };
30 |
31 | export default withWebId(PrivateRoute);
32 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/ColorPicker/color-picker.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText } from '@testing-library/dom';
4 | import ColorPicker from './color-picker.component';
5 | import 'jest-dom/extend-expect';
6 | import { UI } from '@inrupt/lit-generated-vocab-common';
7 |
8 | afterAll(cleanup);
9 |
10 | test('Renders without crashing', () => {
11 | const data = {};
12 | const { container } = render( );
13 | expect(container).toBeTruthy();
14 | });
15 |
16 | test('Renders the label', () => {
17 | const data = {
18 | [UI.label]: 'choose color'
19 | };
20 | const { container } = render( );
21 | expect(getByText(container, 'choose color')).toBeTruthy();
22 | });
23 |
24 | test('Renders the color in text', () => {
25 | const data = {
26 | [UI.label]: 'choose color',
27 | [UI.value]: '#aabbcc'
28 | };
29 | const { container } = render( );
30 | expect(getByText(container, '#aabbcc')).toBeTruthy();
31 | });
32 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Float/float.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText, getByLabelText } from '@testing-library/dom';
4 | import { Float } from './float.component';
5 | import 'jest-dom/extend-expect';
6 | import { UI } from '@inrupt/lit-generated-vocab-common';
7 |
8 | afterAll(cleanup);
9 |
10 | test('Renders without crashing', () => {
11 | const data = {};
12 | const { container } = render( );
13 | expect(container).toBeTruthy();
14 | });
15 |
16 | test('Renders the label', () => {
17 | const data = {
18 | [UI.label]: 'float label'
19 | };
20 | const { container } = render( );
21 | expect(getByText(container, 'float label')).toBeTruthy();
22 | });
23 |
24 | test('Renders the float value', () => {
25 | const data = {
26 | [UI.value]: '123.43',
27 | [UI.label]: 'float label'
28 | };
29 |
30 | const { container } = render( );
31 | expect(getByLabelText(container, 'float label').value).toBe('123.43');
32 | });
33 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Input/input.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText, getByLabelText } from '@testing-library/dom';
4 | import { Input } from './input.component';
5 | import 'jest-dom/extend-expect';
6 | import { UI } from '@inrupt/lit-generated-vocab-common';
7 |
8 | afterAll(cleanup);
9 |
10 | test('Renders without crashing', () => {
11 | const data = {};
12 | const { container } = render( );
13 | expect(container).toBeTruthy();
14 | });
15 |
16 | test('Renders the label', () => {
17 | const data = {
18 | [UI.label]: 'input label'
19 | };
20 | const { container } = render( );
21 | expect(getByText(container, 'input label')).toBeTruthy();
22 | });
23 |
24 | test('Renders the input content', () => {
25 | const data = {
26 | [UI.label]: 'input label',
27 | [UI.value]: 'any content'
28 | };
29 | const { container } = render( );
30 | expect(getByLabelText(container, 'input label').value).toBe('any content');
31 | });
32 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Email/email.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText, getByLabelText } from '@testing-library/dom';
4 | import { Email } from './email.component';
5 | import 'jest-dom/extend-expect';
6 | import { UI } from '@inrupt/lit-generated-vocab-common';
7 |
8 | afterAll(cleanup);
9 |
10 | test('Renders without crashing', () => {
11 | const data = {};
12 | const { container } = render( );
13 | expect(container).toBeTruthy();
14 | });
15 |
16 | test('Renders the label', () => {
17 | const data = {
18 | [UI.label]: 'email label'
19 | };
20 | const { container } = render( );
21 | expect(getByText(container, 'email label')).toBeTruthy();
22 | });
23 |
24 | test('Renders the email address', () => {
25 | const data = {
26 | [UI.label]: 'email label',
27 | [UI.value]: 'test@email.com'
28 | };
29 | const { container } = render( );
30 | expect(getByLabelText(container, 'email label').value).toBe('test@email.com');
31 | });
32 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Integer/integer.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText, getByLabelText } from '@testing-library/dom';
4 | import { Integer } from './integer.component';
5 | import 'jest-dom/extend-expect';
6 | import { UI } from '@inrupt/lit-generated-vocab-common';
7 |
8 | afterAll(cleanup);
9 |
10 | test('Renders without crashing', () => {
11 | const data = {};
12 | const { container } = render( );
13 | expect(container).toBeTruthy();
14 | });
15 |
16 | test('Renders the label', () => {
17 | const data = {
18 | [UI.label]: 'integer label'
19 | };
20 | const { container } = render( );
21 | expect(getByText(container, 'integer label')).toBeTruthy();
22 | });
23 |
24 | test('Renders the integer value', () => {
25 | const data = {
26 | [UI.label]: 'integer label',
27 | [UI.value]: '123'
28 | };
29 | const { container } = render( );
30 | expect(getByLabelText(container, 'integer label').value).toBe('123');
31 | });
32 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Phone/phone.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText, getByLabelText } from '@testing-library/dom';
4 | import { Phone } from './phone.component';
5 | import 'jest-dom/extend-expect';
6 | import { UI } from '@inrupt/lit-generated-vocab-common';
7 |
8 | afterAll(cleanup);
9 |
10 | test('Renders without crashing', () => {
11 | const data = {};
12 | const { container } = render( );
13 | expect(container).toBeTruthy();
14 | });
15 |
16 | test('Renders the label', () => {
17 | const data = {
18 | [UI.label]: 'Phone label'
19 | };
20 | const { container } = render( );
21 | expect(getByText(container, 'Phone label')).toBeTruthy();
22 | });
23 |
24 | test('Renders the Phone address', () => {
25 | const data = {
26 | [UI.label]: 'Phone label',
27 | [UI.value]: '(555) 555-5555'
28 | };
29 | const { container } = render( );
30 | expect(getByLabelText(container, 'Phone label').value).toBe('(555) 555-5555');
31 | });
32 |
--------------------------------------------------------------------------------
/src/lib/components/Uploader/uploader.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import Uploader from './uploader.component';
4 |
5 | const BasicComponent = () => Basic Component
;
6 |
7 | afterAll(cleanup);
8 |
9 | describe('Solid Uploader', () => {
10 | describe('render without crashing', () => {
11 | it('should render uploader component', () => {
12 | const { container } = render( } />);
13 | expect(container).toBeTruthy();
14 | });
15 |
16 | it('should render component by prop', () => {
17 | const { getByTestId } = render( } />);
18 | const childComponent = getByTestId('render-component');
19 | expect(childComponent).toBeTruthy();
20 | });
21 |
22 | it('should render input file', () => {
23 | const { getByTestId } = render( } />);
24 | const inputEl = getByTestId('input-file');
25 | expect(inputEl).toBeTruthy();
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Inrupt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/DropDownField/dropdown-field.component.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ShexConfig } from '@context';
3 | import { cleanup, render } from '@testing-library/react';
4 | import DropDownField from './dropdown-field.component';
5 | import 'jest-dom/extend-expect';
6 |
7 | afterAll(cleanup);
8 |
9 | const config = { theme: {}, languageTheme: {}, config: {} };
10 |
11 | const setup = props => {
12 | return (
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | describe('Shex ShapeForm Component', () => {
20 | const component = setup({ values: ['option1', ['option 1']] });
21 | const { container, rerender } = render(component);
22 |
23 | it('should renders without crashing', () => {
24 | expect(container).toBeTruthy();
25 | });
26 |
27 | it('should renders with object values', () => {
28 | const component = setup({
29 | values: [{ value: 'option1' }, { value: 'option 2' }]
30 | });
31 |
32 | rerender(component);
33 | expect(container).toBeTruthy();
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/SingleLine/single-line.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText, getByLabelText } from '@testing-library/dom';
4 | import { SingleLine } from './single-line.component';
5 | import { UI } from '@inrupt/lit-generated-vocab-common';
6 |
7 | import 'jest-dom/extend-expect';
8 |
9 | afterAll(cleanup);
10 |
11 | test('Renders without crashing', () => {
12 | const data = {};
13 | const { container } = render( );
14 | expect(container).toBeTruthy();
15 | });
16 |
17 | test('Renders the label', () => {
18 | const data = {
19 | [UI.label]: 'single'
20 | };
21 | const { container } = render( );
22 | expect(getByText(container, 'single')).toBeTruthy();
23 | });
24 |
25 | test('Renders the value', () => {
26 | const data = {
27 | [UI.label]: 'single',
28 | [UI.value]: 'test value'
29 | };
30 |
31 | const { container } = render( );
32 | expect(getByLabelText(container, 'single').textContent).toBe('test value');
33 | });
34 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/RadioButton/radio-button.component.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from 'react';
2 | import { ThemeContext } from '@context';
3 | import { UI } from '@inrupt/lit-generated-vocab-common';
4 |
5 | type Props = {
6 | id: string,
7 | data: object,
8 | updateData: (string, string) => void
9 | };
10 |
11 | export const RadioButton = (props: Props) => {
12 | const { id, data, updateData } = props;
13 | const { theme } = useContext(ThemeContext);
14 |
15 | const { [UI.label]: label, [UI.value]: initialValue } = data;
16 |
17 | const [value, setValue] = useState(initialValue);
18 |
19 | const onChange = event => setValue(event.target.value);
20 |
21 | const onBlur = () => {
22 | const updatedPart = { ...data, value };
23 | updateData(id, updatedPart);
24 | };
25 |
26 | return (
27 |
28 | {label}
29 |
38 |
39 | );
40 | };
41 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/TextArea/text-area.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText, getByLabelText } from '@testing-library/dom';
4 | import { TextArea } from './text-area.component';
5 | import 'jest-dom/extend-expect';
6 | import { UI } from '@inrupt/lit-generated-vocab-common';
7 |
8 | afterAll(cleanup);
9 |
10 | test('Renders without crashing', () => {
11 | const data = {};
12 | const { container } = render();
13 | expect(container).toBeTruthy();
14 | });
15 |
16 | test('Renders the label', () => {
17 | const data = {
18 | [UI.label]: 'text area label'
19 | };
20 | const { container } = render();
21 | expect(getByText(container, 'text area label')).toBeTruthy();
22 | });
23 |
24 | test('Renders the text area value', () => {
25 | const data = {
26 | [UI.label]: 'text area label',
27 | [UI.value]: 'Lorem ipsum'
28 | };
29 | const { container } = render();
30 | expect(getByLabelText(container, 'text area label').value).toBe('Lorem ipsum');
31 | });
32 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/RadioButton/radio-button.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByLabelText } from '@testing-library/dom';
4 | import { RadioButton } from './radio-button.component';
5 | import 'jest-dom/extend-expect';
6 | import { UI } from '@inrupt/lit-generated-vocab-common';
7 |
8 | afterAll(cleanup);
9 |
10 | test('should render without crashing', () => {
11 | const { container } = render( );
12 | expect(container).toBeTruthy();
13 | });
14 |
15 | test('should render a label', () => {
16 | const data = {
17 | [UI.label]: '123'
18 | };
19 | const { container } = render( );
20 |
21 | expect(getByLabelText(container, '123')).toBeTruthy();
22 | });
23 |
24 | test('should render a selected radio button', () => {
25 | const data = {
26 | [UI.label]: '123',
27 | [UI.value]: true
28 | };
29 | const { container } = render( );
30 |
31 | const radioElement = getByLabelText(container, '123');
32 | expect(radioElement.value).toBeTruthy();
33 | });
34 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/MultiLine/multi-line.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText, getByLabelText } from '@testing-library/dom';
4 | import MultiLine from './multi-line.component';
5 | import { UI } from '@inrupt/lit-generated-vocab-common';
6 |
7 | import 'jest-dom/extend-expect';
8 |
9 | afterAll(cleanup);
10 |
11 | test('Renders without crashing', () => {
12 | const data = {};
13 | const { container } = render( );
14 | expect(container).toBeTruthy();
15 | });
16 |
17 | test('Renders the label', () => {
18 | const data = {
19 | [UI.label]: 'multi'
20 | };
21 | const { container } = render( );
22 | expect(getByText(container, 'multi')).toBeTruthy();
23 | });
24 |
25 | test('Renders the value', () => {
26 | const data = {
27 | [UI.label]: 'multi',
28 | [UI.value]: `test\nvalue`
29 | };
30 |
31 | const { container } = render( );
32 | const multiLineContainer = getByLabelText(container, 'multi');
33 | expect(multiLineContainer.textContent).toBe('test\nvalue');
34 | });
35 |
--------------------------------------------------------------------------------
/stories/ProviderLogin/provider-login.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { storiesOf } from '@storybook/react';
3 | import { withKnobs, text, object } from '@storybook/addon-knobs';
4 | import { ProviderLogin } from '@solid-react-components';
5 | import ProviderLoginReadme from './README.md';
6 |
7 | storiesOf('Provider Login', module)
8 | .addDecorator(withKnobs)
9 | .addParameters({
10 | readme: {
11 | // Show readme at the addons panel
12 | sidebar: ProviderLoginReadme
13 | },
14 | jest: ['provider-login.container.test.js']
15 | })
16 | .add('default', () => (
17 |
30 | ));
31 |
--------------------------------------------------------------------------------
/src/lib/components/ProfileUploader/profile-uploader.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup, getByTestId } from '@testing-library/react';
3 | import ProfileUploader from './profile-uploader.component';
4 | import 'jest-dom/extend-expect';
5 |
6 | describe('should render without crashing', () => {
7 | afterAll(cleanup);
8 |
9 | it('should render component', () => {
10 | const { container } = render( );
11 | expect(container).toBeTruthy();
12 | });
13 |
14 | it('should render No image without upload file', () => {
15 | const { container } = render( );
16 | expect(container).toHaveTextContent('Upload File');
17 | });
18 |
19 | it('should render image if was uploaded', () => {
20 | const { getByTestId } = render( );
21 | const ImageEl = getByTestId('image-style');
22 |
23 | expect(ImageEl).toBeInTheDocument();
24 | });
25 |
26 | it('should render upload button', () => {
27 | const { getByTestId } = render( );
28 | const ImageEl = getByTestId('button-style');
29 |
30 | expect(ImageEl).toBeInTheDocument();
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/BoolLine/bool-line.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import '@testing-library/jest-dom/extend-expect';
3 | import { render, screen } from '@testing-library/react';
4 | import { getByLabelText } from '@testing-library/dom';
5 |
6 | import { BoolLine } from './bool-line.component';
7 | import { UI } from '@inrupt/lit-generated-vocab-common';
8 |
9 | test('Renders with value and label', async () => {
10 | const data = {
11 | [UI.value]: '82346-123438-asdf8324',
12 | [UI.label]: 'Not a default content'
13 | };
14 | render( );
15 |
16 | expect(screen.getByText('Not a default content')).toBeInTheDocument();
17 | });
18 |
19 | test('Renders without a label', async () => {
20 | const data = { [UI.value]: '345' };
21 | const { container } = render( );
22 | expect(container.firstChild.firstChild).toBeEmpty();
23 | });
24 |
25 | test('Renders without a value', async () => {
26 | const data = { [UI.label]: 'Label for the bool' };
27 | const { container } = render( );
28 | expect(getByLabelText(container, 'Label for the bool').checked).toBeFalsy();
29 | });
30 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/DeleteButton/delete-button.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 | import { ShexConfig } from '@context';
4 |
5 | const DeleteButtonWrapper = styled.button`
6 | position: ${({ floating }) => (floating ? 'absolute' : 'relative')};
7 | right: 8px;
8 | color: red;
9 | border: none;
10 | background: none;
11 | cursor: pointer;
12 | z-index: 1;
13 | `;
14 |
15 | type Props = {
16 | predicate: String,
17 | fieldData: Object,
18 | parent: Object,
19 | text: String
20 | };
21 |
22 | const DeleteButton = (props: Props) => {
23 | const { fieldData, predicate, parent, text = 'Remove' } = props;
24 | return (
25 |
26 | {({ theme, languageTheme: { deleteButton }, config: { onDelete } }) => (
27 | onDelete(predicate ? { ...fieldData, predicate } : fieldData, parent)}
31 | floating={parent}
32 | >
33 | {deleteButton || text}
34 |
35 | )}
36 |
37 | );
38 | };
39 |
40 | export default DeleteButton;
41 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/TextArea/text-area.component.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext } from 'react';
2 | import { UI } from '@inrupt/lit-generated-vocab-common';
3 | import { ThemeContext } from '@context';
4 | import { TextAreaGroup } from './text-area.styles';
5 |
6 | type Props = {
7 | id: string,
8 | data: object,
9 | updateData: (string, string) => void
10 | };
11 |
12 | export const TextArea = (props: Props) => {
13 | const { id, data, updateData } = props;
14 | const { theme } = useContext(ThemeContext);
15 |
16 | const { [UI.label]: label, [UI.maxLength]: maxLength, [UI.value]: initialValue } = data;
17 |
18 | const [value, setValue] = useState(initialValue);
19 |
20 | const onChange = event => setValue(event.target.value);
21 |
22 | const onBlur = () => {
23 | const updatedPart = { ...data, value };
24 | updateData(id, updatedPart);
25 | };
26 |
27 | return (
28 |
29 | {label}
30 |
39 |
40 | );
41 | };
42 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Input/input.component.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from 'react';
2 | import { RDF, UI } from '@inrupt/lit-generated-vocab-common';
3 | import { ThemeContext } from '@context';
4 | import { InputTextTypes } from '@constants';
5 | import { InputGroup } from './input.styles';
6 |
7 | export const Input = props => {
8 | const { id, data, updateData } = props;
9 | const { theme } = useContext(ThemeContext);
10 |
11 | const {
12 | [UI.label]: label,
13 | [UI.maxLength]: maxLength,
14 | [RDF.type]: type,
15 | [UI.value]: initialValue
16 | } = data;
17 |
18 | const [value, setValue] = useState(initialValue);
19 |
20 | const onChange = event => {
21 | setValue(event.target.value);
22 | };
23 |
24 | const onBlur = () => {
25 | const updatedPart = { ...data, value };
26 | updateData(id, updatedPart);
27 | };
28 |
29 | return (
30 |
31 | {label}
32 |
42 |
43 | );
44 | };
45 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/expression-fields.component.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import { shexUtil } from '@utils';
3 | import { AddButton, Field } from '.';
4 |
5 | type FieldsProps = {
6 | data: Object,
7 | formValues: Object,
8 | parent: Object
9 | };
10 |
11 | const ExpressionFields = ({ data, formValues, parent }: FieldsProps) => {
12 | return (
13 |
14 |
15 | {data._formValues &&
16 | data._formValues.map((value, i) => (
17 | // eslint-disable-next-line react/no-array-index-key
18 |
19 |
28 |
29 | ))}
30 |
31 |
38 |
39 | );
40 | };
41 |
42 | export default ExpressionFields;
43 |
--------------------------------------------------------------------------------
/src/lib/components/PrivateRoute/private-route.component.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { MemoryRouter } from 'react-router-dom';
3 | import { render, cleanup } from '@testing-library/react';
4 | import { PrivateRoute } from './private-route.component';
5 |
6 | import 'jest-dom/extend-expect';
7 |
8 | afterAll(cleanup);
9 |
10 | describe('Private Route', () => {
11 | const defaultWeb = 'https://example.org/#me';
12 | const { container, rerender } = render(
13 |
14 |
15 |
16 | );
17 |
18 | it('should render loading when user is not logged in', () => {
19 | expect(container).toHaveTextContent('We are validating your data...');
20 | });
21 |
22 | it('should not render loader when user is logged in', () => {
23 | rerender(
24 |
25 |
26 |
27 | );
28 | });
29 |
30 | it('should not render loader when user is logged', () => {
31 | rerender(
32 |
33 |
34 |
35 | );
36 |
37 | expect(container).not.toHaveTextContent('We are validating your data...');
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/src/lib/entities/index.js:
--------------------------------------------------------------------------------
1 | export type Provider = {
2 | description: String,
3 | image: String,
4 | label: String,
5 | value: String
6 | };
7 |
8 | export type SelectOptions = {
9 | label: string,
10 | value: string
11 | };
12 |
13 | export type UploadedFiles = {
14 | uri: String,
15 | name: String
16 | };
17 |
18 | export type SolidError = {
19 | type: String,
20 | statusText: String,
21 | code: number
22 | };
23 |
24 | export type Annotation = {
25 | type: String,
26 | predicate: String,
27 | object: {}
28 | };
29 |
30 | export type FormFocus = {
31 | value: String,
32 | name: String,
33 | parentSubject: ?String,
34 | parentPredicate: ?String
35 | };
36 |
37 | export type FormValue = {
38 | _formFocus: FormFocus
39 | };
40 |
41 | export type Expressions = {
42 | annotations: ?Array,
43 | predicate: String,
44 | type: String,
45 | valueExpr: any,
46 | _formValues: ?Array
47 | };
48 |
49 | export type Expression = {
50 | expressions: Array,
51 | type: String,
52 | _formFocus: ?FormFocus
53 | };
54 |
55 | export type Shape = {
56 | expression: ?Expression,
57 | id: String,
58 | type: String
59 | };
60 |
61 | export type ShexJ = {
62 | '@context': String,
63 | shapes: Array,
64 | start: ?String,
65 | type: String,
66 | expression: ?Expression
67 | };
68 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/MultipleViewer/multiple-viewer.component.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { UI } from '@inrupt/lit-generated-vocab-common';
3 | import { ThemeContext } from '@context';
4 | import Viewer from '../../viewer.component';
5 |
6 | type Props = {
7 | key: string,
8 | formModel: object,
9 | parent: object
10 | };
11 |
12 | export const MultipleViewer = (props: Props) => {
13 | const { key, formModel, parent } = props;
14 | const { theme } = useContext(ThemeContext);
15 | const { [UI.label]: label, [UI.part]: part } = formModel;
16 | const parts = [];
17 |
18 | // Get list of parts for the
19 | if (part) {
20 | Object.keys(part).forEach(item => {
21 | parts.push(part[item]);
22 | });
23 | }
24 |
25 | return (
26 |
27 |
{label}
28 | {parts.map(item => {
29 | // Fetch the name from the object for a unique key
30 | const key = item[UI.name];
31 | return (
32 |
33 |
39 |
40 | );
41 | })}
42 |
43 | );
44 | };
45 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/CheckBox/check-box.styles.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Label = styled.label`
4 | cursor: pointer;
5 | overflow: hidden;
6 | padding: 0.5em 1em;
7 | & > input[type='checkbox'] {
8 | display: none;
9 | }
10 |
11 | & {
12 | display: flex;
13 | height: 1em;
14 | width: fit-content;
15 | align-items: center;
16 | position: relative;
17 | &::before,
18 | &::after {
19 | content: '';
20 | border-radius: 0;
21 | box-sizing: border-box;
22 | }
23 |
24 | &::before {
25 | left: 0;
26 | width: 1em;
27 | height: 1em;
28 | margin: 0 8px 0 0;
29 | background: #f7f7f7;
30 | box-shadow: 0 0 1px grey;
31 | display: inline-block;
32 | }
33 | &::after {
34 | left: 1.5px;
35 | width: 0.8em;
36 | height: 0.8em;
37 | opacity: 0;
38 | background: #37b2b2;
39 | transform: translate3d(-20px, 0, 0) scale(0.2);
40 | transition: opacity 0.25s ease-in-out, transform 0.25s ease-in-out;
41 | position: absolute;
42 | }
43 | }
44 |
45 | &:checked + .label-text {
46 | &::after {
47 | transform: translate3d(0, 0, 0);
48 | opacity: 1;
49 | }
50 | }
51 | }
52 | `;
53 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/component-mapping.js:
--------------------------------------------------------------------------------
1 | import { UI } from '@inrupt/lit-generated-vocab-common';
2 |
3 | import { Heading } from './Heading';
4 | import { Comment } from './Comment';
5 | import { Input } from './Input';
6 | import { DateTimePicker } from './DateTimePicker';
7 | import { CheckBox } from './CheckBox';
8 | import { Decimal } from './Decimal';
9 | import { TextArea } from './TextArea';
10 | import { Float } from './Float';
11 | import { Email } from './Email';
12 | import { Phone } from './Phone';
13 | import { Integer } from './Integer';
14 | import { ColorPicker } from './ColorPicker';
15 | import { Multiple } from '../../Multiple';
16 | import { Group } from '../../Group';
17 | import Classifier from './Classifier';
18 |
19 | export const Mapping = {
20 | [UI.Heading]: Heading,
21 | [UI.Comment]: Comment,
22 | [UI.SingleLineTextField]: Input,
23 | [UI.IntegerField]: Integer,
24 | [UI.DateField]: DateTimePicker,
25 | [UI.DateTimeField]: DateTimePicker,
26 | [UI.TimeField]: DateTimePicker,
27 | [UI.BooleanField]: CheckBox,
28 | [UI.DecimalField]: Decimal,
29 | [UI.MultiLineTextField]: TextArea,
30 | [UI.FloatField]: Float,
31 | [UI.EmailField]: Email,
32 | [UI.PhoneField]: Phone,
33 | [UI.ColorField]: ColorPicker,
34 | [UI.Multiple]: Multiple,
35 | [UI.Group]: Group,
36 | [UI.Classifier]: Classifier
37 | };
38 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/BoolLine/bool-line.style.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Label = styled.label`
4 | cursor: pointer;
5 | overflow: hidden;
6 | padding: 0.5em 1em;
7 | & > input[type='checkbox'] {
8 | display: none;
9 |
10 | & + .label-text {
11 | display: flex;
12 | height: 1em;
13 | width: fit-content;
14 | align-items: center;
15 | position: relative;
16 | &::before,
17 | &::after {
18 | content: '';
19 | border-radius: 0;
20 | box-sizing: border-box;
21 | }
22 |
23 | &::before {
24 | left: 0;
25 | width: 1em;
26 | height: 1em;
27 | margin: 0 8px 0 0;
28 | background: #f7f7f7;
29 | box-shadow: 0 0 1px grey;
30 | display: inline-block;
31 | }
32 | &::after {
33 | left: 1.5px;
34 | width: 0.8em;
35 | height: 0.8em;
36 | opacity: 0;
37 | background: #37b2b2;
38 | transform: translate3d(-20px, 0, 0) scale(0.2);
39 | transition: opacity 0.25s ease-in-out, transform 0.25s ease-in-out;
40 | position: absolute;
41 | }
42 | }
43 |
44 | &:checked + .label-text {
45 | &::after {
46 | transform: translate3d(0, 0, 0);
47 | opacity: 1;
48 | }
49 | }
50 | }
51 | `;
52 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Decimal/decimal.component.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext } from 'react';
2 | import { RDF, UI } from '@inrupt/lit-generated-vocab-common';
3 | import { InputTextTypes } from '@constants';
4 | import { ThemeContext } from '@context';
5 | import { InputGroup } from '../Input/input.styles';
6 |
7 | export const Decimal = props => {
8 | const { id, data, updateData } = props;
9 | const { theme } = useContext(ThemeContext);
10 |
11 | const {
12 | [RDF.type]: type,
13 | [UI.label]: label,
14 | [UI.maxLength]: maxLength,
15 | [UI.minValue]: minValue,
16 | [UI.size]: size,
17 | [UI.value]: initialValue
18 | } = data;
19 |
20 | const [value, setValue] = useState(initialValue);
21 |
22 | const onChange = event => {
23 | setValue(event.target.value);
24 | };
25 |
26 | const onBlur = () => {
27 | const updatedPart = { ...data, value };
28 | updateData(id, updatedPart);
29 | };
30 |
31 | return (
32 |
33 | {label}
34 |
46 |
47 | );
48 | };
49 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/CheckBox/check-box.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText, getByLabelText } from '@testing-library/dom';
4 | import { UI } from '@inrupt/lit-generated-vocab-common';
5 | import { CheckBox } from './check-box.component';
6 | import 'jest-dom/extend-expect';
7 |
8 | afterAll(cleanup);
9 |
10 | test('Renders without crashing', () => {
11 | const data = {};
12 | const { container } = render( );
13 | expect(container).toBeTruthy();
14 | });
15 |
16 | test('Renders the label', () => {
17 | const data = {
18 | [UI.label]: 'check'
19 | };
20 | const { container } = render( );
21 | expect(getByText(container, 'check')).toBeTruthy();
22 | });
23 |
24 | test('Renders a checked box', () => {
25 | const data = {
26 | [UI.label]: 'checklabel',
27 | [UI.value]: 'true'
28 | };
29 | const { container } = render( );
30 | expect(getByLabelText(container, 'checklabel').checked).toBeTruthy();
31 | });
32 |
33 | test('Renders an unchecked box', () => {
34 | const data = {
35 | [UI.label]: 'checklabel',
36 | [UI.value]: 'false'
37 | };
38 | const { container } = render( );
39 | expect(getByLabelText(container, 'checklabel').checked).toBeFalsy();
40 | });
41 |
--------------------------------------------------------------------------------
/src/lib/components/ProviderLogin/provider-login.container.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup, fireEvent, getByTestId } from '@testing-library/react';
3 | import auth from 'solid-auth-client';
4 | import ProviderLogin from './provider-login.container';
5 | import 'jest-dom/extend-expect';
6 |
7 | afterAll(cleanup);
8 |
9 | describe('Provider Login Container', () => {
10 | it('shoud renders without crashing', () => {
11 | const { container } = render( );
12 | expect(container).toBeTruthy();
13 | });
14 |
15 | it('should render WebId by default', () => {
16 | const { getByTestId } = render( );
17 | const selectEl = getByTestId('input-webid');
18 |
19 | expect(selectEl).toBeInTheDocument();
20 | });
21 |
22 | it('clicking link button should render Provider Select', () => {
23 | const { container, getByTestId } = render( );
24 | const button = getByTestId('change-mode-button');
25 |
26 | fireEvent.click(button);
27 |
28 | expect(container).toHaveTextContent('Select ID Provider');
29 | });
30 |
31 | it('should not call login without webId or provider', async () => {
32 | const { getByTestId } = render( );
33 | const formButtonEl = getByTestId('provider-form-button');
34 |
35 | fireEvent.click(formButtonEl);
36 |
37 | expect(auth.login).toBeCalledTimes(0);
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Float/float.component.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from 'react';
2 | import { RDF, UI } from '@inrupt/lit-generated-vocab-common';
3 | import { InputTextTypes } from '@constants';
4 | import { ThemeContext } from '@context';
5 | import { InputGroup } from '../Input/input.styles';
6 |
7 | type Props = {
8 | id: string,
9 | data: object,
10 | updateData: (string, string) => void
11 | };
12 |
13 | export const Float = (props: Props) => {
14 | const { id, data, updateData } = props;
15 | const { theme } = useContext(ThemeContext);
16 |
17 | const {
18 | [RDF.type]: type,
19 | [UI.label]: label,
20 | [UI.maxLength]: maxLength,
21 | [UI.minValue]: minValue,
22 | [UI.size]: size,
23 | [UI.value]: initialValue
24 | } = data;
25 |
26 | const [value, setValue] = useState(initialValue);
27 |
28 | const onChange = event => setValue(event.target.value);
29 |
30 | const onBlur = () => {
31 | const updatedPart = { ...data, value };
32 | updateData(id, updatedPart);
33 | };
34 |
35 | return (
36 |
37 | {label}
38 |
50 |
51 | );
52 | };
53 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Phone/phone.component.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext } from 'react';
2 | import { RDF, UI } from '@inrupt/lit-generated-vocab-common';
3 | import { InputTextTypes } from '@constants';
4 | import { ThemeContext } from '@context';
5 | import { InputGroup } from '../Input/input.styles';
6 |
7 | type Props = {
8 | id: string,
9 | data: object,
10 | updateData: (string, string) => void
11 | };
12 |
13 | export const Phone = (props: Props) => {
14 | const { id, data, updateData } = props;
15 | const { theme } = useContext(ThemeContext);
16 |
17 | const {
18 | [RDF.type]: type,
19 | [UI.label]: label,
20 | [UI.maxLength]: maxLength,
21 | [UI.size]: size,
22 | [UI.pattern]: pattern,
23 | [UI.value]: initialValue
24 | } = data;
25 |
26 | const [value, setValue] = useState(initialValue);
27 |
28 | const onChange = event => setValue(event.target.value);
29 |
30 | const onBlur = () => {
31 | const updatedPart = { ...data, value };
32 | updateData(id, updatedPart);
33 | };
34 |
35 | return (
36 |
37 | {label}
38 |
50 |
51 | );
52 | };
53 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Email/email.component.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext } from 'react';
2 | import { RDF, UI } from '@inrupt/lit-generated-vocab-common';
3 | import { InputTextTypes } from '@constants';
4 |
5 | import { ThemeContext } from '@context';
6 | import { InputGroup } from '../Input/input.styles';
7 |
8 | type Props = {
9 | id: string,
10 | data: object,
11 | updateData: (string, string) => void
12 | };
13 |
14 | export const Email = (props: Props) => {
15 | const { id, data, updateData } = props;
16 | const { theme } = useContext(ThemeContext);
17 |
18 | const {
19 | [RDF.type]: type,
20 | [UI.label]: label,
21 | [UI.maxLength]: maxLength,
22 | [UI.size]: size,
23 | [UI.pattern]: pattern,
24 | [UI.value]: initialValue
25 | } = data;
26 |
27 | const [value, setValue] = useState(initialValue);
28 |
29 | const onChange = event => setValue(event.target.value);
30 |
31 | const onBlur = () => {
32 | const updatedPart = { ...data, value };
33 | updateData(id, updatedPart);
34 | };
35 |
36 | return (
37 |
38 | {label}
39 |
51 |
52 | );
53 | };
54 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Integer/integer.component.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext } from 'react';
2 | import { RDF, UI } from '@inrupt/lit-generated-vocab-common';
3 | import { InputTextTypes } from '@constants';
4 | import { ThemeContext } from '@context';
5 | import { InputGroup } from '../Input/input.styles';
6 |
7 | type Props = {
8 | id: string,
9 | data: object,
10 | updateData: (string, string) => void
11 | };
12 |
13 | export const Integer = (props: Props) => {
14 | const { id, data, updateData } = props;
15 | const { theme } = useContext(ThemeContext);
16 |
17 | const {
18 | [RDF.type]: type,
19 | [UI.label]: label,
20 | [UI.minValue]: minValue,
21 | [UI.size]: size,
22 | [UI.value]: initialValue
23 | } = data;
24 |
25 | const [value, setValue] = useState(initialValue);
26 |
27 | const onChange = event => {
28 | const re = /^[0-9]+$/;
29 | if (event.target.value === '' || re.test(event.target.value)) setValue(event.target.value);
30 | };
31 |
32 | const onBlur = () => {
33 | const updatedPart = { ...data, value };
34 | updateData(id, updatedPart);
35 | };
36 |
37 | return (
38 |
39 | {label}
40 |
52 |
53 | );
54 | };
55 |
--------------------------------------------------------------------------------
/src/lib/components/ProviderSelect/provider.select.component.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React from 'react';
3 | import Select, { components } from 'react-select';
4 | import { SelectOptions } from '@entities';
5 |
6 | import { Item, Icon, ItemText } from './styled.components';
7 |
8 | type Props = {
9 | className: String,
10 | placeholder: string,
11 | options: Array,
12 | onChange: () => void,
13 | components: Array
14 | };
15 |
16 | const Option = ({ innerProps, isDisabled, innerRef, data }): React.Component =>
17 | !isDisabled ? (
18 | -
19 |
20 | {data.label}
21 |
22 | ) : null;
23 |
24 | const SingleValue = ({ data, ...props }): React.Component => {
25 | return (
26 |
27 |
28 | {data.label}
29 |
30 | );
31 | };
32 |
33 | const ProviderSelect = (props: Props) => {
34 | const { className, placeholder, options, components, onChange } = props;
35 | return (
36 |
46 | );
47 | };
48 |
49 | export default ProviderSelect;
50 |
--------------------------------------------------------------------------------
/src/lib/hooks/useNotification.test.js:
--------------------------------------------------------------------------------
1 | import { useNotification } from '@hooks';
2 | import { Notification } from '@classes';
3 | import { renderHook } from '@testing-library/react-hooks';
4 | import { cleanup } from '@testing-library/react';
5 | import { act } from 'react-dom/test-utils';
6 |
7 | const webIdExample = 'https://example.org/#me';
8 |
9 | jest.mock('../../lib/classes/notification');
10 |
11 | const setup = () => {
12 | let component = null;
13 |
14 | act(() => {
15 | component = renderHook(() => useNotification(webIdExample));
16 | });
17 |
18 | return component;
19 | };
20 |
21 | describe('useNotification', () => {
22 | afterAll(() => cleanup);
23 |
24 | beforeEach(() => {
25 | // Clear all instances and calls to constructor and all methods:
26 | Notification.mockClear();
27 | });
28 |
29 | it('returns empty notifications on init', async () => {
30 | const { result } = setup();
31 |
32 | expect(result.current.notification.notifications).toEqual([]);
33 | expect(result.current.notification.originalNotifications).toEqual([]);
34 | });
35 |
36 | it('should called the class Notification on init', () => {
37 | const NotificationInstance = new Notification();
38 | expect(Notification).toHaveBeenCalledTimes(1);
39 | });
40 |
41 | it('called a method on the class instance', () => {
42 | const { hookConsumerNotification } = setup();
43 |
44 | const notificationConsumer = hookConsumerNotification;
45 | // Constructor should have been called again:
46 | expect(Notification).toHaveBeenCalledTimes(1);
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/CheckBox/check-box.component.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState, useContext } from 'react';
2 | import { UI } from '@inrupt/lit-generated-vocab-common';
3 | import { ThemeContext } from '@context';
4 | import { InputGroup } from '../Input/input.styles';
5 |
6 | type Props = {
7 | id: string,
8 | data: object,
9 | updateData: (string, string) => void
10 | };
11 |
12 | export const CheckBox = (props: Props) => {
13 | const { id, data, updateData } = props;
14 | const { theme } = useContext(ThemeContext);
15 |
16 | const [checked, setChecked] = useState(false);
17 |
18 | useEffect(() => {
19 | try {
20 | const podValue = data[UI.value];
21 | if (!podValue || podValue === 'false') {
22 | setChecked(false);
23 | } else {
24 | setChecked(true);
25 | }
26 | } catch (e) {
27 | setChecked(false);
28 | }
29 | }, [data[UI.value]]);
30 |
31 | const { [UI.label]: label } = data;
32 |
33 | const onChange = event => {
34 | const updatedPart = { ...data, value: String(event.target.checked) };
35 | updateData(id, updatedPart);
36 | setChecked(event.target.checked);
37 | };
38 |
39 | return (
40 |
41 |
42 | {label}
43 |
44 |
53 |
54 | );
55 | };
56 |
--------------------------------------------------------------------------------
/src/lib/components/ProfileUploader/profile-uploader.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { UploadedFiles } from '@entities';
3 | import { ProfileWrapper, ImgStyle, ButtonStyle } from './profile-uploader.style';
4 |
5 | /**
6 | * Basic Uploader UI Component Example
7 | */
8 |
9 | type Props = {
10 | onDrag: () => void,
11 | onDrop: () => void,
12 | onDragLeave: () => void,
13 | onClickFile: () => void,
14 | overrideEventDefaults: () => void,
15 | uploadedFiles: Array,
16 | uploadedFiles: Array,
17 | className: String
18 | };
19 |
20 | const ProfileUploader = (props: Props) => {
21 | const {
22 | overrideEventDefaults,
23 | onDragLeave,
24 | onDragEnter,
25 | onDrop,
26 | className,
27 | uploadedFiles,
28 | onClickFile
29 | } = props;
30 | return (
31 |
43 | {uploadedFiles && uploadedFiles.length > 0 && (
44 |
49 | )}
50 |
51 | Upload File
52 |
53 |
54 | );
55 | };
56 |
57 | export default ProfileUploader;
58 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/AddButton/add-button.component.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ShexConfig } from '@context';
3 | import { cleanup, render } from '@testing-library/react';
4 | import AddButton from './add-button.component';
5 | import 'jest-dom/extend-expect';
6 |
7 | afterAll(cleanup);
8 |
9 | const config = {
10 | languageTheme: {
11 | language: 'en',
12 | addButtonText: '+ Add new '
13 | },
14 | theme: {},
15 | config: {}
16 | };
17 |
18 | const defaultExpression = {
19 | annotations: []
20 | };
21 |
22 | const setup = (props, config) => {
23 | return (
24 |
25 |
26 |
27 | );
28 | };
29 |
30 | describe('Shex ShapeForm Component', () => {
31 | const component = setup({ allowNewFields: true, defaultExpression }, config);
32 | const { container, rerender } = render(component);
33 |
34 | it('should renders without crashing', () => {
35 | expect(container).toBeTruthy();
36 | });
37 |
38 | it('should renders language version', () => {
39 | const languageTheme = {
40 | language: 'es',
41 | addButtonText: '+ Agregar '
42 | };
43 | const component = setup(
44 | { allowNewFields: true, defaultExpression },
45 | { ...config, languageTheme }
46 | );
47 |
48 | rerender(component);
49 | expect(container).toHaveTextContent('+ Agregar');
50 | });
51 |
52 | it('should not renders if allowNewField is false', () => {
53 | const component = setup({ allowNewFields: false, defaultExpression }, config);
54 | rerender(component);
55 |
56 | expect(container).toHaveTextContent('');
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/DeleteButton/delete-button.component.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ShexConfig } from '@context';
3 | import { cleanup, render } from '@testing-library/react';
4 | import DeleteButton from './delete-button.component';
5 | import 'jest-dom/extend-expect';
6 |
7 | afterAll(cleanup);
8 |
9 | const defaultExpression = {
10 | annotations: []
11 | };
12 |
13 | const setup = (props, config) => {
14 | return (
15 |
16 |
17 |
18 | );
19 | };
20 |
21 | describe('Shex ShapeForm Component', () => {
22 | const config = {
23 | theme: {},
24 | languageTheme: {
25 | language: 'en',
26 | addButtonText: '+ Add new '
27 | },
28 | config: {}
29 | };
30 |
31 | const component = setup({ canDelete: true, defaultExpression }, config);
32 | const { container, rerender } = render(component);
33 |
34 | it('should renders without crashing', () => {
35 | expect(container).toBeTruthy();
36 | });
37 |
38 | it('should renders language version', () => {
39 | const config = {
40 | theme: {},
41 | config: {},
42 | languageTheme: {
43 | language: 'es',
44 | deleteButton: 'Eliminar'
45 | }
46 | };
47 | const component = setup({ canDelete: true, defaultExpression }, config);
48 |
49 | rerender(component);
50 | expect(container).toHaveTextContent('Eliminar');
51 | });
52 |
53 | it('should not renders if canDelete is false', () => {
54 | const component = setup({ canDelete: false, defaultExpression }, config);
55 | rerender(component);
56 |
57 | expect(container).toHaveTextContent('');
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/src/lib/classes/access-control-list.test.js:
--------------------------------------------------------------------------------
1 | import AccessControlList from './access-control-list';
2 | import ldflex from '../../test/__mocks__/@solid/query-ldflex';
3 |
4 | const webIdExample = 'https://example.org/#me';
5 | const fileExample = 'https://example.org/public/test.ttl';
6 | const aclExample = 'https://example.org/public/test.ttl.acl';
7 |
8 | describe('Access Control List test', () => {
9 | beforeAll(() => {
10 | jest.restoreAllMocks();
11 | });
12 |
13 | const PERMISSIONS = {
14 | APPEND: 'Append',
15 | READ: 'Read',
16 | WRITE: 'Write',
17 | CONTROL: 'Control'
18 | };
19 |
20 | const ACLInstance = new AccessControlList(webIdExample, fileExample, aclExample);
21 |
22 | it('Modes are correct', async () => {
23 | expect(AccessControlList.MODES).toEqual(PERMISSIONS);
24 | });
25 |
26 | it('Instance is correct', async () => {
27 | expect(ACLInstance.aclUri).toEqual(`${fileExample}.acl`);
28 | expect(ACLInstance.acl).toBe(null);
29 | });
30 |
31 | it('Get subjects from a doc', async () => {
32 | const doc = ldflex[webIdExample];
33 | const result = await ACLInstance.getSubjects(doc);
34 | expect(result).toBeTruthy();
35 | });
36 |
37 | it('Creates permissions turtle', async () => {
38 | const permissions = [{ agents: [webIdExample], modes: [AccessControlList.MODES.READ] }];
39 | const result = await ACLInstance.createPermissionsTurtle(permissions);
40 |
41 | expect(result).not.toBe(null);
42 | });
43 |
44 | it('Get permissions', async () => {
45 | const permissions = [{ agents: [webIdExample], modes: [AccessControlList.MODES.READ] }];
46 | const result = await ACLInstance.getPermissions(permissions);
47 | expect(result).not.toBe(null);
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/DateLine/date-line.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { format } from 'date-fns';
3 |
4 | import { RDF, UI } from '@inrupt/lit-generated-vocab-common';
5 | import { FormModelConfig } from '@context';
6 | import { getClosestLocale } from '@utils';
7 |
8 | import { Wrapper, Label, Value } from './date-line.style';
9 |
10 | type Props = {
11 | data: object,
12 | formModel: object,
13 | parent: object,
14 | name: String
15 | };
16 |
17 | const DateLine = (props: Props) => {
18 | const { data, formModel, parent, name } = props;
19 | const type = data[RDF.type];
20 | const locale = getClosestLocale();
21 | const value = data[UI.value];
22 |
23 | let renderValue;
24 | try {
25 | if (type === UI.DateTimeField.value) {
26 | renderValue = format(new Date(value), 'Pp', { locale });
27 | }
28 |
29 | if (type === UI.DateField.value) {
30 | const [year, month, day] = value.split('-').map(n => Number(n));
31 | renderValue = format(new Date(year, month - 1, day), 'P', { locale });
32 | }
33 |
34 | if (type === UI.TimeField.value) {
35 | const [hours, minutes, seconds] = value.split(':').map(n => Number(n));
36 | renderValue = format(new Date(2000, 0, 1, hours, minutes, seconds), 'p', { locale });
37 | }
38 | } catch {
39 | renderValue = '';
40 | }
41 |
42 | return (
43 |
44 | {({ theme }) => (
45 |
46 | {data[UI.label]}
47 | {renderValue}
48 |
49 | )}
50 |
51 | );
52 | };
53 |
54 | export default DateLine;
55 |
--------------------------------------------------------------------------------
/src/lib/shapes/notification.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "default",
3 | "@context": {
4 | "terms": "http://purl.org/dc/terms#",
5 | "as": "https://www.w3.org/ns/activitystreams#",
6 | "schema": "http://schema.org/",
7 | "solid": "https://www.w3.org/ns/solid/terms#",
8 | "xsd": "http://www.w3.org/2001/XMLSchema#",
9 | "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
10 | },
11 | "shape": [
12 | {
13 | "label": "title",
14 | "property": "terms:title",
15 | "type": "Literal",
16 | "datatype": "string"
17 | },
18 | {
19 | "label": "summary",
20 | "property": "as:summary",
21 | "type": "Literal",
22 | "datatype": "string"
23 | },
24 | {
25 | "label": "actor",
26 | "property": "as:actor",
27 | "type": "NamedNode",
28 | "datatype": "IRI"
29 | },
30 | {
31 | "label": "target",
32 | "property": "as:target",
33 | "type": "NamedNode",
34 | "datatype": "IRI"
35 | },
36 | {
37 | "label": "object",
38 | "property": "as:object",
39 | "type": "NamedNode",
40 | "datatype": "IRI"
41 | },
42 | {
43 | "label": "image",
44 | "property": "schema:image",
45 | "type": "NamedNode",
46 | "datatype": "IRI"
47 | },
48 | {
49 | "label": "read",
50 | "property": "solid:read",
51 | "value": false,
52 | "type": "Literal",
53 | "datatype": "boolean"
54 | },
55 | {
56 | "label": "published",
57 | "property": "as:published",
58 | "type": "Literal",
59 | "datatype": "datetime"
60 | },
61 | {
62 | "label": "license",
63 | "property": "schema:license",
64 | "type": "Literal",
65 | "datatype": "IRI"
66 | }
67 | ]
68 | }
69 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/DateLine/date-line.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { getByText, getByLabelText } from '@testing-library/dom';
4 | import DateLine from './date-line.component';
5 | import 'jest-dom/extend-expect';
6 | import { RDF, UI } from '@inrupt/lit-generated-vocab-common';
7 |
8 | afterAll(cleanup);
9 |
10 | test('Renders without crashing', () => {
11 | const data = {};
12 | const { container } = render( );
13 | expect(container).toBeTruthy();
14 | });
15 |
16 | test('Renders the label', () => {
17 | const data = {
18 | [UI.label]: 'date label'
19 | };
20 | const { container } = render( );
21 | expect(getByText(container, 'date label')).toBeTruthy();
22 | });
23 |
24 | describe('Renders the value depending on the type', () => {
25 | it('Renders a time', () => {
26 | const data = {
27 | [RDF.type]: UI.TimeField.value,
28 | [UI.value]: '09:12:44'
29 | };
30 |
31 | const { container } = render( );
32 |
33 | expect(getByText(container, '9:12 AM')).toBeTruthy();
34 | });
35 |
36 | it('Renders a date', () => {
37 | const data = {
38 | [RDF.type]: UI.DateField.value,
39 | [UI.value]: '2020-01-13'
40 | };
41 |
42 | const { container } = render( );
43 | expect(getByText(container, '01/13/2020')).toBeTruthy();
44 | });
45 |
46 | it('Renders a datetime', () => {
47 | const data = {
48 | [RDF.type]: UI.DateTimeField.value,
49 | [UI.value]: '2020-01-13 09:12:44'
50 | };
51 |
52 | const { container } = render( );
53 | expect(getByText(container, '01/13/2020, 9:12 AM')).toBeTruthy();
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/ColorPicker/color-picker.component.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useContext, useState } from 'react';
2 | import { ChromePicker } from 'react-color';
3 | import { UI } from '@inrupt/lit-generated-vocab-common';
4 | import { ThemeContext } from '@context';
5 |
6 | import { PickerGroup, ColorSwatch, Cover, Popover } from './color-picker.styles';
7 |
8 | type Props = {
9 | id: string,
10 | data: object,
11 | updateData: (string, string) => void
12 | };
13 |
14 | const ColorPicker = (props: Props) => {
15 | const { id, data, updateData } = props;
16 | const { theme } = useContext(ThemeContext);
17 |
18 | const { [UI.label]: label, [UI.value]: initialValue } = data;
19 |
20 | const [pickerVisible, setPickerVisible] = useState(false);
21 | const [color, setColor] = useState(initialValue);
22 |
23 | const handleChange = color => setColor(color.hex);
24 |
25 | const handleChangeComplete = color => {
26 | setColor(color.hex);
27 | const updatedPart = { ...data, value: color.hex };
28 | updateData(id, updatedPart);
29 | };
30 |
31 | const handleClick = () => {
32 | setPickerVisible(!pickerVisible);
33 | };
34 |
35 | const handleClose = () => {
36 | setPickerVisible(false);
37 | };
38 |
39 | return (
40 |
41 | {label}
42 | {color}
43 |
44 | {pickerVisible ? (
45 |
46 |
47 |
53 |
54 | ) : null}
55 |
56 | );
57 | };
58 |
59 | export default ColorPicker;
60 |
--------------------------------------------------------------------------------
/src/lib/components/ShexFormBuilder/shex-form-builder.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { LiveUpdate } from '@solid/react';
3 | import ShexFormLive from './children/ShexFormLive';
4 |
5 | type Props = {
6 | errorCallback: () => void,
7 | successCallback: () => void,
8 | messageValidation: { error: Array },
9 | documentUri: String,
10 | shexUri: String,
11 | rootShape: String,
12 | theme: Object,
13 | languageTheme: Object
14 | };
15 |
16 | const ShexFormBuilder = ({
17 | successCallback,
18 | errorCallback,
19 | documentUri,
20 | shexUri,
21 | rootShape,
22 | theme,
23 | languageTheme,
24 | autoSaveMode
25 | }: Props) => {
26 | const subscribeUri = documentUri && documentUri !== '' ? documentUri.replace(/#.*/, '') : '';
27 |
28 | return (
29 | subscribeUri && (
30 |
31 |
43 |
44 | )
45 | );
46 | };
47 |
48 | ShexFormBuilder.defaultProps = {
49 | // eslint-disable-next-line no-console
50 | successCallback: () => console.log('Submitted successfully'),
51 | // eslint-disable-next-line no-console
52 | errorCallback: e => console.error('Status:', e.status || e.code, e.message),
53 | theme: {
54 | input: 'solid-input-shex',
55 | select: 'solid-input-shex solid-select-shex',
56 | deleteButton: 'solid-button-shex',
57 | form: 'solid-shex-form'
58 | },
59 | languageTheme: {
60 | language: 'en',
61 | saveBtn: 'Save',
62 | resetBtn: 'Reset',
63 | addButtonText: '+ Add new ',
64 | deleteButton: 'Delete',
65 | dropdownDefaultText: '- Select -'
66 | }
67 | };
68 |
69 | export default ShexFormBuilder;
70 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Group/group.component.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { RDF, UI } from '@inrupt/lit-generated-vocab-common';
3 | import { ThemeContext } from '@context';
4 |
5 | type Props = {
6 | data: object,
7 | updateData: (string, string) => void,
8 | mapper: object,
9 | addNewField: string => void,
10 | deleteField: string => void,
11 | savingData: {
12 | autosaveIndicator: React.Component,
13 | running: boolean,
14 | names: Array,
15 | error: boolean
16 | }
17 | };
18 |
19 | export const Group = (props: Props) => {
20 | const { data, updateData, mapper, savingData, addNewField, deleteField } = props;
21 | const { theme } = useContext(ThemeContext);
22 |
23 | return (
24 |
25 | {Object.entries(data).map(([, part]) => {
26 | const { [RDF.type]: type, [UI.name]: name } = part;
27 | const Component = mapper[type];
28 |
29 | if (!Component) return null;
30 |
31 | /* if this component is being saved right now */
32 | const savingThis = savingData.names.some((componentName: string) => name === componentName);
33 | const componentData = type === UI.Group.iriAsString ? part[UI.parts] : part;
34 |
35 | let Indicator = () => null;
36 | if (savingData && savingThis) Indicator = savingData.autosaveIndicator;
37 |
38 | return (
39 |
40 |
52 |
53 |
54 | );
55 | })}
56 |
57 | );
58 | };
59 |
--------------------------------------------------------------------------------
/src/lib/utils/datestimes.test.js:
--------------------------------------------------------------------------------
1 | import { parseInitialValue, isValidDate } from './datetimes';
2 | import { UI } from '@inrupt/lit-generated-vocab-common';
3 |
4 | describe('Parser should return the expected values', () => {
5 | const time = '19:00:34';
6 | const date = '2012-12-12';
7 | const dateTime = '2019-11-29T04:00:00.000Z';
8 |
9 | it('should parse times correctly', () => {
10 | const [hours, minutes, seconds] = time.split(':').map(i => parseInt(i, 10));
11 | const result = parseInitialValue(time, UI.TimeField.iriAsString);
12 |
13 | expect(result.getHours()).toBe(hours);
14 | expect(result.getMinutes()).toBe(minutes);
15 | expect(result.getSeconds()).toBe(seconds);
16 | });
17 |
18 | it('should parse dates correctly', () => {
19 | const [year, month, day] = date.split('-').map(i => parseInt(i, 10));
20 | const result = parseInitialValue(date, UI.DateField.iriAsString);
21 |
22 | expect(result.getFullYear()).toEqual(year);
23 | // Months start at 0
24 | expect(result.getMonth()).toEqual(month - 1);
25 | expect(result.getDate()).toEqual(day);
26 | });
27 |
28 | it('should parse datetimes correctly', () => {
29 | expect(parseInitialValue(dateTime, UI.DateTimeField.iriAsString)).toEqual(new Date(dateTime));
30 | });
31 | });
32 |
33 | describe('Datetime checker should validate values', () => {
34 | it('should validate now as a date', () => {
35 | expect(isValidDate(new Date())).toEqual(true);
36 | });
37 |
38 | it('should fail with anything not a date object', () => {
39 | expect(isValidDate('string')).toEqual(false);
40 | expect(isValidDate(123)).toEqual(false);
41 | expect(isValidDate({})).toEqual(false);
42 | expect(isValidDate([new Date()])).toEqual(false);
43 | expect(isValidDate({ date: new Date() })).toEqual(false);
44 | });
45 |
46 | it('should return false with a "Invalid date" Date object', () => {
47 | expect(isValidDate(new Date('not a date representation'))).toBe(false);
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/src/demo/components/HandleShexForm/handle-shex-form.component.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import styled from 'styled-components';
3 | import { ShexFormBuilder } from '@components';
4 |
5 | const Form = styled.form``;
6 |
7 | const Input = styled.input`
8 | margin: 20px 0;
9 | padding: 10px;
10 | width: 100%
11 | box-sizing: border-box;
12 | `;
13 |
14 | const Button = styled.button`
15 | margin: 20px 10px;
16 | border: 1px solid hsl(0, 0%, 80%);
17 | cursor: pointer;
18 | padding: 10px 30px;
19 | `;
20 |
21 | type Props = {
22 | webId: String
23 | };
24 |
25 | const HandleShexForm = ({ webId }: Props) => {
26 | const [shexFormConfig, setShexFormConfig] = useState({});
27 | const [podData, setPodData] = useState({
28 | documentUri: webId,
29 | shexUri: 'https://shexshapes.inrupt.net/public/userprofile.shex'
30 | });
31 |
32 | const onChangeInput = (e: Event) => {
33 | setShexFormConfig({ ...shexFormConfig, [e.target.name]: e.target.value });
34 | };
35 |
36 | const onSubmit = (e: Event) => {
37 | e.preventDefault();
38 |
39 | setPodData({ ...shexFormConfig });
40 | };
41 | // By default addButton and deleteButton will be Add new and Delete
42 | const languageTheme = {
43 | language: 'en',
44 | saveBtn: 'Save',
45 | resetBtn: 'Reset',
46 | addButtonText: '+ Add new ',
47 | deleteButton: 'Delete',
48 | dropdownDefaultText: '- Select -',
49 | warningResolution: 'Field value has been updated to:'
50 | };
51 |
52 | return (
53 |
54 |
Shex Form
55 |
60 |
61 |
62 | );
63 | };
64 |
65 | export default HandleShexForm;
66 |
--------------------------------------------------------------------------------
/src/lib/classes/notifications.test.js:
--------------------------------------------------------------------------------
1 | import { Notification } from './notification';
2 |
3 | const webIdExample = 'https://example.org/#me';
4 | const inboxExample = 'https://example.org/inbox/';
5 |
6 | describe('Notification test', () => {
7 | beforeAll(() => {
8 | jest.restoreAllMocks();
9 | });
10 |
11 | const NotificationInstance = new Notification(null);
12 |
13 | it('should set owner to webId Example', async () => {
14 | // Owner should be null
15 | expect(NotificationInstance.owner).toBe(null);
16 |
17 | // After set owner should be webIdExample
18 | NotificationInstance.setOwner(webIdExample);
19 |
20 | expect(NotificationInstance.owner).toBe(webIdExample);
21 | });
22 |
23 | it('hasInbox should return boolean', async () => {
24 | const result = await NotificationInstance.hasInbox(webIdExample);
25 | expect(result).toBe(true);
26 | });
27 |
28 | it('should return error on delete inbox when path is empty', async () => {
29 | const result = await NotificationInstance.deleteInbox();
30 |
31 | expect(result.code).toBe(500);
32 | });
33 |
34 | it('should return 200 when delete notification', async () => {
35 | const result = await NotificationInstance.deleteInbox(inboxExample, webIdExample);
36 |
37 | expect(result.code).toBe(200);
38 | });
39 |
40 | it('should throw an error when inbox already exist on pod', async () => {
41 | try {
42 | await NotificationInstance.createInbox(inboxExample, webIdExample);
43 | } catch (error) {
44 | expect(error.message).toBe('Inbox already exist');
45 | }
46 | });
47 |
48 | it('should return code 200 on delete notification file', async () => {
49 | const result = await NotificationInstance.delete(`${inboxExample}/notification.ttl`);
50 | expect(result.code).toBe(200);
51 | });
52 |
53 | it('should return a shape object', () => {
54 | const result = NotificationInstance.buildShapeObject({ name: 'shapeName', path: 'shapePath' });
55 | const mockObject = { name: 'shapeName', shape: 'shapePath' };
56 | expect(result).toEqual(mockObject);
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/src/lib/components/ProviderLogin/children/Form/form.presentational.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 | import { ProviderSelect } from '@components';
4 | import { SolidInput, SolidLinkButton, SolidButton, ErrorMessage } from '@styled-components';
5 |
6 | const LoginFormWrapper = styled.div`
7 | button {
8 | margin: 20px auto;
9 | display: block;
10 | }
11 | `;
12 |
13 | const LoginForm = props => {
14 | const {
15 | className,
16 | onSubmit,
17 | error,
18 | withWebId,
19 | selectPlaceholder,
20 | onSelectChange,
21 | providers,
22 | onChangeInput,
23 | inputPlaceholder,
24 | optionToggle,
25 | btnTxtProvider,
26 | btnTxtWebId,
27 | formButtonText,
28 | theme
29 | } = props;
30 | return (
31 |
32 |
66 |
67 | );
68 | };
69 |
70 | export default LoginForm;
71 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/UI/ui-mapping.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { UI } from '@inrupt/lit-generated-vocab-common';
4 | import { SingleLine } from './SingleLine';
5 | import MultiLine from './MultiLine';
6 | import DateLine from './DateLine';
7 | import { BoolLine } from './BoolLine';
8 | import ColorLine from './ColorLine';
9 | import { Heading } from '../../Form/UI/Heading';
10 | import { Comment } from '../../Form/UI/Comment';
11 |
12 | const UIMapping = type => {
13 | let component;
14 | switch (type) {
15 | case UI.SingleLineTextField.iriAsString:
16 | component = SingleLine;
17 | break;
18 | case UI.MultiLineTextField.iriAsString:
19 | component = MultiLine;
20 | break;
21 | case UI.DecimalField.iriAsString:
22 | component = SingleLine;
23 | break;
24 | case UI.FloatField.iriAsString:
25 | component = SingleLine;
26 | break;
27 | case UI.IntegerField.iriAsString:
28 | component = SingleLine;
29 | break;
30 | case UI.EmailField.iriAsString:
31 | component = SingleLine;
32 | break;
33 | case UI.PhoneField.iriAsString:
34 | component = SingleLine;
35 | break;
36 | case UI.TriStateField.iriAsString:
37 | component = SingleLine;
38 | break;
39 | case UI.BooleanField.iriAsString:
40 | component = BoolLine;
41 | break;
42 | case UI.ColorField.iriAsString:
43 | component = ColorLine;
44 | break;
45 | case UI.DateField.iriAsString:
46 | component = props => ;
47 | break;
48 | case UI.DateTimeField.iriAsString:
49 | component = props => ;
50 | break;
51 | case UI.TimeField.iriAsString:
52 | component = props => ;
53 | break;
54 | case UI.Classifier.iriAsString:
55 | component = SingleLine;
56 | break;
57 | case UI.Heading.iriAsString:
58 | component = Heading;
59 | break;
60 | case UI.Comment.iriAsString:
61 | component = Comment;
62 | break;
63 | default:
64 | component = null;
65 | }
66 | return component;
67 | };
68 |
69 | export default UIMapping;
70 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/DateTimePicker/date-time-picker.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { DateTimePicker } from './date-time-picker.component';
4 | import 'jest-dom/extend-expect';
5 | import { RDF, UI } from '@inrupt/lit-generated-vocab-common';
6 |
7 | afterAll(cleanup);
8 |
9 | test('Time picker should render', () => {
10 | const data = {
11 | formObject: {},
12 | value: '11:00:15',
13 | [RDF.type]: UI.TimeField.iriAsString
14 | };
15 |
16 | const { container } = render( );
17 | expect(container).toBeTruthy();
18 | });
19 |
20 | test('Date picker should render', () => {
21 | const data = {
22 | formObject: {},
23 | value: '2011-06-06',
24 | [RDF.type]: UI.DateField.iriAsString
25 | };
26 |
27 | const { container } = render( );
28 | expect(container).toBeTruthy();
29 | });
30 |
31 | test('Datetime picker should render', () => {
32 | const data = {
33 | formObject: {},
34 | value: '2019-11-29T04:00:00.000Z',
35 | [RDF.type]: UI.DateTimeField.iriAsString
36 | };
37 |
38 | const { container } = render( );
39 | expect(container).toBeTruthy();
40 | });
41 |
42 | test('Datetime picker should render with undefined value', () => {
43 | const data = {
44 | formObject: {},
45 | value: undefined,
46 | [RDF.type]: UI.DateTimeField.iriAsString
47 | };
48 |
49 | const { container } = render( );
50 | expect(container).toBeTruthy();
51 | });
52 |
53 | test('Timepicker should render with undefined value', () => {
54 | const data = {
55 | formObject: {},
56 | value: undefined,
57 | [RDF.type]: UI.TimeField.iriAsString
58 | };
59 |
60 | const { container } = render( );
61 | expect(container).toBeTruthy();
62 | });
63 |
64 | test('Date picker should render with undefined value', () => {
65 | const data = {
66 | formObject: {},
67 | value: undefined,
68 | [RDF.type]: UI.DateField.iriAsString
69 | };
70 |
71 | const { container } = render( );
72 | expect(container).toBeTruthy();
73 | });
74 |
--------------------------------------------------------------------------------
/stories/ProviderLogin/README.md:
--------------------------------------------------------------------------------
1 | ### ProviderLogin
2 |
3 | The ProviderLogin component is primarily a Login Form component. Using it in your application will provide a relatively unstyled login form, complete with a dropdown of potential Solid Providers for users to select from.
4 |
5 | For now, the list of Providers is passed in as a parameter. In the future, this could include an option to fetch Providers from a registry. Without a Provider, the user will not be able to login, so this should help accelerate application development.
6 |
7 | ```javascript
8 |
9 | ```
10 |
11 | | Props | Type | Default | Description |
12 | | ----------------- | ------------------------------------------------------------------------ | --------------------------- | ----------------------------------- |
13 | | providers | Array of Providers (label, image, value, register link, and description) | Solid and Inrupt Providers. |
14 | | callback | Function | null | Will call after login. |
15 | | className | String | null | Custom class for component. |
16 | | OnError | Function | null | If an error occurs, this will fire. |
17 | | selectPlaceholder | String | Select ID Provider |
18 | | inputPlaceholder | String | WebID |
19 | | formButtonText | String | Log In |
20 | | btnTxtWebId | String | Log In with WebID |
21 | | btnTxtProvider | String | Log In with Provider |
22 | | errorsText | Object |
23 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/Field/field.component.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable jsx-a11y/label-has-for */
2 | import React, { Fragment } from 'react';
3 | import { shexUtil } from '@utils';
4 | import { ShexConfig } from '@context';
5 | import InputField from '../InputField';
6 | import DropDownField from '../DropDownField';
7 |
8 | type FieldProps = {
9 | data: Object,
10 | inputData: Object
11 | };
12 |
13 | const Field = ({ data, fieldData, inputData, canDelete, parent }: FieldProps) => {
14 | const inputType = data.valueExpr.values ? 'select' : 'text';
15 | const { predicate } = data;
16 | const annotation = shexUtil.findAnnotation('layoutprefix', data.annotations);
17 | const hasPrefix = annotation && annotation.object.value;
18 | const parentPredicate = parent && parent.predicate ? parent.predicate : null;
19 | const parentSubject = parent && parent._formFocus.parentSubject;
20 | return (
21 |
22 | {({ languageTheme: { language } }) => (
23 |
24 | {shexUtil.formLabel(data, language)}
25 | {inputType === 'text' ? (
26 |
42 | ) : (
43 |
61 | )}
62 |
63 | )}
64 |
65 | );
66 | };
67 |
68 | export default Field;
69 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Form/UI/Classifier/classifier.component.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useCallback, useContext } from 'react';
2 | import { n3Helper } from '@inrupt/solid-sdk-forms';
3 | import unique from 'unique';
4 | import { UI } from '@inrupt/lit-generated-vocab-common';
5 | import { ThemeContext } from '@context';
6 | import { SelectWrapper } from './classifier.style';
7 |
8 | export const Classifier = props => {
9 | const { id, data, updateData } = props;
10 | const { theme } = useContext(ThemeContext);
11 |
12 | const {
13 | [UI.label]: label,
14 | [UI.value]: initialValue,
15 | [UI.category]: category,
16 | [UI.values]: values
17 | } = data;
18 |
19 | const [options, setOptions] = useState([]);
20 | const [value, setValue] = useState(initialValue);
21 |
22 | /**
23 | * Init function to make use of async
24 | * This function fetches the classifier option list from a helper, or returns the list of hardcoded values
25 | * if such a list exists as a prop
26 | * @returns {Promise}
27 | */
28 | const init = async () => {
29 | let optionsList = [];
30 | if (category) {
31 | optionsList = await n3Helper.getClassifierOptions(category);
32 | } else {
33 | optionsList = values ? [...values] : [];
34 | }
35 | setOptions(optionsList);
36 | };
37 |
38 | /**
39 | * Initialize data for the dropdown
40 | */
41 | useEffect(() => {
42 | init();
43 | }, []);
44 |
45 | /**
46 | * Fetch a user-friendly label for the option. If the option is a link, this returns the predicate name
47 | * @type {function(string): string}
48 | */
49 | const getDropDownLabel = useCallback((value: string) => {
50 | return value && value.includes('#') ? value.split('#')[1] : value;
51 | });
52 |
53 | const onChange = async event => {
54 | await setValue(event.target.value);
55 | };
56 |
57 | useEffect(() => {
58 | if (value !== initialValue) {
59 | const updatedPart = { ...data, value };
60 | updateData(id, updatedPart);
61 | }
62 | }, [value]);
63 |
64 | return (
65 |
66 | {label}
67 |
68 | {options.map(option => (
69 |
70 | {getDropDownLabel(option)}
71 |
72 | ))}
73 |
74 |
75 | );
76 | };
77 |
78 | export default Classifier;
79 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/DropDownField/dropdown-field.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import unique from 'unique-string';
3 | import { ShexConfig } from '@context';
4 | import DeleteButton from '../DeleteButton';
5 | import { ErrorMessage, SelectWrapper } from './styled.component';
6 |
7 | const DropDownField = ({
8 | value,
9 | values,
10 | name,
11 | disabled,
12 | error,
13 | defaultValue,
14 | subject,
15 | predicate,
16 | hasPrefix,
17 | parentPredicate,
18 | parent,
19 | canDelete,
20 | fieldData
21 | }) => {
22 | return (
23 |
24 | {({
25 | theme,
26 | languageTheme,
27 | config: { onChange, onDelete, onSubmitSave, autoSaveMode, isValueChanged }
28 | }) => (
29 |
30 |
38 | autoSaveMode &&
39 | isValueChanged(value, defaultValue, name) &&
40 | onSubmitSave(name, 'autoSave')
41 | }
42 | data-predicate={predicate}
43 | data-subject={subject}
44 | data-default={defaultValue}
45 | data-prefix={hasPrefix}
46 | data-parent-predicate={parentPredicate}
47 | >
48 |
49 | {(languageTheme && languageTheme.dropdownDefaultText) || '-- Select an option --'}
50 |
51 | {values &&
52 | values.map(val => {
53 | const uVal = typeof val === 'string' ? val.split('#')[1] : val.value;
54 | const selectValue = typeof val === 'string' ? val : val.value;
55 |
56 | return (
57 |
58 | {uVal}
59 |
60 | );
61 | })}
62 |
63 | {error && {error} }
64 | {!parent && canDelete && (
65 |
74 | )}
75 |
76 | )}
77 |
78 | );
79 | };
80 |
81 | export default DropDownField;
82 |
--------------------------------------------------------------------------------
/src/lib/components/withAuthorization/with-authorization.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import auth from 'solid-auth-client';
3 | import { MemoryRouter } from 'react-router-dom';
4 | import { render, cleanup } from '@testing-library/react';
5 | import { withAuthorization } from '@components';
6 | import 'jest-dom/extend-expect';
7 |
8 | const ComponentExample = () => Component Example
;
9 | const ComponentLoader = () => Loader Component
;
10 |
11 | describe('A withWebId wrapper', () => {
12 | const defaultWeb = 'https://example.org/#me';
13 | const Wrapper = withAuthorization(ComponentExample, );
14 |
15 | /**
16 | * This is a workaround to avoid console warning message on react-dom.
17 | * If you want to know more about please go to:
18 | * https://github.com/facebook/react/issues/14769#issuecomment-470097212
19 | */
20 |
21 | const originalError = console.error;
22 |
23 | beforeAll(() => {
24 | console.error = jest.fn();
25 | });
26 |
27 | afterAll(() => {
28 | console.error = originalError;
29 | cleanup();
30 | });
31 |
32 | describe('before a session is received', () => {
33 | it('renders the loader component', () => {
34 | const { container } = render(
35 |
36 |
37 |
38 | );
39 |
40 | expect(container).toHaveTextContent('Loader Component');
41 | });
42 | });
43 |
44 | describe('when the user is not logged in', () => {
45 | beforeAll(() => auth.mockWebId(null));
46 |
47 | /**
48 | * Removed the check for Loader component due to an unexpected race condition (or similar) when loading this test
49 | * The test was executing when the webId was still being set, and so it was loading the Loading component when it was undefined, then
50 | * immediately getting set to null. No code has changed to cause this behavior so it remains a tiny mystery
51 | */
52 | it('redirect user', () => {
53 | const { container } = render(
54 |
55 |
56 |
57 | );
58 |
59 | expect(container).not.toHaveTextContent('Component Example');
60 | // expect(container).not.toHaveTextContent('Loader Component');
61 | });
62 | });
63 |
64 | describe('when the user is logged in', () => {
65 | beforeAll(() => auth.mockWebId(defaultWeb));
66 |
67 | it('renders the wrapped component', () => {
68 | const { container } = render(
69 |
70 |
71 |
72 | );
73 |
74 | expect(container).toHaveTextContent('Component Example');
75 | });
76 | });
77 | });
78 |
--------------------------------------------------------------------------------
/src/lib/components/ShexForm/children/InputField/input-field.component.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ShexConfig } from '@context';
3 | import DeleteButton from '../DeleteButton';
4 | import { ErrorMessage, InputWrapper, Input, InputGroup } from './styled.component';
5 |
6 | const InputField = ({
7 | type = 'text',
8 | valueExpr,
9 | predicate,
10 | inputData,
11 | hasPrefix,
12 | parentPredicate,
13 | parentSubject,
14 | parent,
15 | canDelete,
16 | fieldData
17 | }) => {
18 | const inputName = inputData && inputData.name;
19 | const defaultValue = fieldData && fieldData._formFocus.value;
20 | const currentValue = inputData && inputData.value;
21 | const disabled = inputData && inputData.disabled;
22 |
23 | return (
24 |
25 | {({ theme, config: { onChange, onSubmitSave, autoSaveMode, isValueChanged } }) => (
26 |
31 |
32 |
49 | autoSaveMode &&
50 | isValueChanged(currentValue, defaultValue, inputName) &&
51 | onSubmitSave(inputName, 'autoSave')
52 | }
53 | />
54 | {!parent && canDelete && (
55 |
61 | )}
62 |
63 | {inputData && (inputData.error || inputData.warning) && (
64 |
65 | {inputData.error || inputData.warning}
66 |
67 | )}
68 |
69 | )}
70 |
71 | );
72 | };
73 |
74 | export default InputField;
75 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Multiple/multiple.component.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 |
3 | import { UI } from '@inrupt/lit-generated-vocab-common';
4 | import { ThemeContext } from '@context';
5 | import { Group } from '../Group';
6 | import { DeleteButton } from '../Form/UI/DeleteButton/delete-button.component';
7 |
8 | type Props = {
9 | id: string,
10 | data: object,
11 | updateData: (string, string) => void,
12 | mapper: object,
13 | addNewField: string => void,
14 | deleteField: string => void,
15 | savingData: {
16 | autosaveIndicator: React.Component,
17 | running: boolean,
18 | names: Array,
19 | error: boolean
20 | }
21 | };
22 |
23 | export const Multiple = (props: Props) => {
24 | const { id, data, updateData, mapper, savingData, addNewField, deleteField } = props;
25 | const { theme } = useContext(ThemeContext);
26 | const { [UI.label]: label, [UI.part]: part } = data;
27 |
28 | const parts = [];
29 |
30 | // Get list of parts for the
31 | if (part) {
32 | Object.keys(part).forEach(item => {
33 | parts.push(part[item]);
34 | });
35 | }
36 |
37 | return (
38 |
39 |
{label}
40 | {parts.map(item => {
41 | // Fetch the name from the object for a unique key
42 | const key = item[UI.name];
43 | const type = UI.Group.iriAsString;
44 |
45 | // For now we only support Multiples containing Groups. Once that restriction goes away we need more checks
46 | // to see if the type is a part or another Component. This groupParts is a temporary fix until we add support
47 | // for more Component types in Multiples
48 | const groupPartsKey = Object.keys(item[UI.parts])[0];
49 | const groupParts = item[UI.parts][groupPartsKey][UI.parts];
50 |
51 | // If the group doesn't contain parts, exit gracefully. This shouldn't get hit with a valid form model.
52 | if (!groupParts) {
53 | return
;
54 | }
55 | return (
56 |
57 |
65 |
73 |
74 | );
75 | })}
76 |
77 |
addNewField(id)} className={theme && theme.multiple}>
78 | Add new field
79 |
80 |
81 | );
82 | };
83 |
--------------------------------------------------------------------------------
/src/lib/utils/datetimes.js:
--------------------------------------------------------------------------------
1 | import { addHours, setHours, setMinutes, setSeconds } from 'date-fns';
2 | import * as locales from 'date-fns/locale';
3 |
4 | import { UI } from '@inrupt/lit-generated-vocab-common';
5 |
6 | /**
7 | * @param value - object stored in the pod
8 | * @param type - one of UI[TimeField, DateField, DateTimeField]
9 | * @returns {Date} - local datetime for the given string
10 | */
11 | export const parseInitialValue = (value: string, type: string): Date => {
12 | if (type === UI.TimeField.iriAsString) {
13 | if (!value) return '';
14 | const tokens = value.split(':');
15 |
16 | let date = new Date();
17 | date = setHours(date, tokens[0]);
18 | date = setMinutes(date, tokens[1]);
19 | date = setSeconds(date, tokens[2]);
20 |
21 | return date;
22 | }
23 | if (type === UI.DateField.iriAsString) {
24 | if (!value) return '';
25 | /* date constructor interprets `value` as a UTC time, instead of a local time.
26 | To convert that we apply the offset in hours.
27 | */
28 | let date = new Date(value);
29 | const offset = date.getTimezoneOffset();
30 |
31 | date = addHours(date, offset / 60);
32 | return date;
33 | }
34 | if (type === UI.DateTimeField.iriAsString) {
35 | return new Date(value);
36 | }
37 |
38 | throw new Error(`Error: Unsupported type: ${type}`);
39 | };
40 |
41 | /**
42 | * @param value: value to check
43 | * @returns {boolean} true if @value is a Date object and not "Invalid date"
44 | */
45 | export const isValidDate = (value: any): boolean => {
46 | if (Object.prototype.toString.call(value) === '[object Date]') {
47 | // see https://stackoverflow.com/a/1353711
48 | return !Number.isNaN(value.getTime());
49 | }
50 | // Not a date
51 | return false;
52 | };
53 |
54 | /**
55 | * @returns {string} - the browser-set locale
56 | */
57 | export const getLocale = (): string => {
58 | if (navigator.languages !== undefined) return navigator.languages[0];
59 | return navigator.language ? navigator.language : 'en-US';
60 | };
61 |
62 | /**
63 | * gets and transform the browser locale to match the date-fns locale name
64 | * e.g.: browser format: `en-US`
65 | * date-fns format: `enUS`
66 | *
67 | * @returns string the matching locale name, if found, enUS otherwise
68 | */
69 | export const getFormattedLocale = (): string => {
70 | const locale = getLocale().split('-');
71 | if (locale.length > 1) {
72 | locale[1] = locale[1].toUpperCase();
73 | return `${locale[0]}${locale[1]}`;
74 | }
75 | return `${locale[0]}`;
76 | };
77 |
78 | /**
79 | * tries to get the closest locale object based on the browser locale
80 | * e.g.: `en-US` -> locales[`enUS`]
81 | * `es-CR` -> locales[`es`]
82 | * @returns {Locale | enUS} the closest found locale object
83 | */
84 | export const getClosestLocale = (): string => {
85 | const firstOption = locales[getFormattedLocale()];
86 | if (firstOption) return firstOption;
87 |
88 | const browserLocale = getLocale();
89 | const secondOption = locales[browserLocale.split('-')[0]];
90 | if (secondOption) return secondOption;
91 |
92 | return locales.enUS;
93 | };
94 |
--------------------------------------------------------------------------------
/src/lib/classes/app-permissions.js:
--------------------------------------------------------------------------------
1 | import solid from 'solid-auth-client';
2 | import * as N3 from 'n3';
3 |
4 | /**
5 | * class to handle app permissions into the pod.
6 | */
7 | class AppPermissions {
8 | constructor() {
9 | this.permissions = {};
10 | }
11 |
12 | /**
13 | * Get ACL modes from object value
14 | * @param quad
15 | * @returns {*}
16 | */
17 | getModes = quad => {
18 | if (quad.predicate.id === 'http://www.w3.org/ns/auth/acl#mode') {
19 | return quad.object.id && quad.object.id.split('#')[1];
20 | }
21 | };
22 |
23 | /**
24 | * Get app permission by webId
25 | * @param webId
26 | * @param cache
27 | * @returns {Promise}
28 | */
29 | checkPermissions = async (webId, cache) => {
30 | const currentLocation = window.location;
31 | const appDomain = new URL(currentLocation).origin;
32 |
33 | await this.getPermissions(webId, cache);
34 | const nodes = Object.keys(this.permissions[webId]);
35 | for (const node of nodes) {
36 | if (this.permissions[webId][node].origin === appDomain) {
37 | return this.permissions[webId][node];
38 | }
39 | }
40 |
41 | return null;
42 | };
43 |
44 | /**
45 | * Get permission by webId
46 | * @param webId
47 | * @param cache
48 | * @returns {Promise}
49 | */
50 | getPermissions = async (webId, cache = true) => {
51 | if (this.permissions[webId] && cache) return;
52 | /**
53 | * Fetch document from pod
54 | */
55 | const result = await solid.fetch(webId);
56 | const document = await result.text();
57 | /**
58 | * Parse turtle to Quads
59 | * @type {N3Parser}
60 | */
61 | const parser = new N3.Parser({ baseIRI: webId });
62 | const nquads = await parser.parse(document.toString());
63 | let nodes = {};
64 |
65 | /**
66 | * Find all the array link node name
67 | */
68 | const restQuads = nquads.filter(quad => {
69 | if (quad.predicate.id === 'http://www.w3.org/ns/auth/acl#trustedApp') {
70 | nodes = { ...nodes, [quad.object.id]: { permissions: [] } };
71 |
72 | return null;
73 | }
74 | return quad;
75 | });
76 | /**
77 | * Run over all the quads an create an object
78 | */
79 | restQuads.forEach(quad => {
80 | if (nodes[quad.subject.id]) {
81 | const permission = this.getModes(quad);
82 | nodes[quad.subject.id] = {
83 | ...nodes[quad.subject.id],
84 | permissions: permission
85 | ? [...nodes[quad.subject.id].permissions, permission]
86 | : nodes[quad.subject.id].permissions
87 | };
88 |
89 | if (quad.predicate.id === 'http://www.w3.org/ns/auth/acl#origin') {
90 | nodes[quad.subject.id] = {
91 | ...nodes[quad.subject.id],
92 | origin: quad.object.id
93 | };
94 | }
95 | }
96 | });
97 |
98 | this.permissions[webId] = nodes;
99 | };
100 | }
101 |
102 | const AppPermission = new AppPermissions();
103 |
104 | export default AppPermission;
105 |
--------------------------------------------------------------------------------
/src/lib/components/FormModel/children/Viewer/viewer.component.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from 'react';
2 | import { RDF, UI } from '@inrupt/lit-generated-vocab-common';
3 | import { ThemeContext } from '@context';
4 | import UIMapping from './UI/ui-mapping';
5 | import { Group } from './viewer.style';
6 | import { MultipleViewer } from './UI/MultipleViewer/multiple-viewer.component';
7 |
8 | type Props = {
9 | formModel: Object,
10 | parent?: any
11 | };
12 |
13 | const Viewer = (props: Props) => {
14 | const { theme } = useContext(ThemeContext);
15 | const { formModel, parent } = props;
16 | const [formFields, setFormFields] = useState([]);
17 | const partsKey = UI.parts.iriAsString;
18 | const parts = formModel[partsKey];
19 |
20 | const getArrayFields = () => {
21 | if (typeof formModel === 'object' && parts) {
22 | setFormFields(Object.keys(parts));
23 | }
24 | };
25 |
26 | useEffect(() => {
27 | getArrayFields();
28 | }, [formModel]);
29 |
30 | return (
31 |
32 | {formModel['dc:title'] && {formModel['dc:title']} }
33 | {formFields.length > 0 &&
34 | formFields.map(item => {
35 | // Grabs the field from the parent list of parts, and checks if we have parts in the new field as well
36 | const field = parts[item];
37 | const type = field[RDF.type];
38 |
39 | // Fetch the component from the Viewer-specific mapper
40 | const Component = field && UIMapping(type);
41 | const id = (field && field[UI.name]) || item;
42 |
43 | /**
44 | * Return null when field doesn't exists
45 | * this avoid to crash app using recursive component
46 | */
47 | if (!field) return null;
48 |
49 | /* eslint no-useless-computed-key: "off" */
50 | const { ['ui:parts']: deleted, ...updatedField } = field;
51 |
52 | return (
53 |
54 | {type === UI.Group.iriAsString && (
55 |
62 | )}
63 | {type === UI.Multiple.iriAsString && (
64 |
71 | )}
72 | {type !== UI.Group.iriAsString && type !== UI.Multiple.iriAsString && (
73 |
82 | )}
83 |
84 | );
85 | })}
86 |
87 | );
88 | };
89 |
90 | Viewer.defaultProps = {
91 | parent: null
92 | };
93 |
94 | export default Viewer;
95 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-dynamic-require */
2 | /* eslint-disable global-require */
3 | const path = require('path');
4 | const fs = require('fs');
5 | const url = require('url');
6 |
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebook/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd());
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 |
12 | const envPublicUrl = process.env.PUBLIC_URL;
13 |
14 | function ensureSlash(inputPath, needsSlash) {
15 | const hasSlash = inputPath.endsWith('/');
16 | if (hasSlash && !needsSlash) {
17 | return inputPath.substr(0, inputPath.length - 1);
18 | }
19 | if (!hasSlash && needsSlash) {
20 | return `${inputPath}/`;
21 | }
22 | return inputPath;
23 | }
24 |
25 | const getPublicUrl = appPackageJson => envPublicUrl || require(appPackageJson).homepage;
26 |
27 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
28 | // "public path" at which the app is served.
29 | // Webpack needs to know it to put the right