Added a new sample react-faq app
This commit is contained in:
parent
fb012d4212
commit
0f13b091ed
|
@ -0,0 +1,25 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
|
||||
# change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# we recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -0,0 +1,32 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
|
||||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
solution
|
||||
temp
|
||||
*.sppkg
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
|
||||
# Visual Studio files
|
||||
.ntvs_analysis.dat
|
||||
.vs
|
||||
bin
|
||||
obj
|
||||
|
||||
# Resx Generated Code
|
||||
*.resx.ts
|
||||
|
||||
# Styles Generated Code
|
||||
*.scss.ts
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"isCreatingSolution": true,
|
||||
"environment": "spo",
|
||||
"version": "1.8.2",
|
||||
"libraryName": "spfxwebparts",
|
||||
"libraryId": "f3b9b2b7-533c-42db-9a81-d3ea68e1e8b6",
|
||||
"packageManager": "npm",
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
## SharePoint Framework Faq App
|
||||
|
||||
|
||||
## Summary
|
||||
- This Web Part allows users to create Frequently Asked Questions(Faq App) in modern and classic SharePoint pages.
|
||||
- This webpart allows to search within questions and answers which are stored in a SharePoint FAQ list.
|
||||
- "React-autosuggest and react-accessible-accordion" react packages are used for the search and accordion control.
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/FAQWebpart.png"/>
|
||||
</p>
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
![drop](https://img.shields.io/badge/drop-1.8.2-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Framework](https:/dev.office.com/sharepoint)
|
||||
* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment)
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
react-FAQApp | Ashok Swain - LinkedIn: https://www.linkedin.com/in/ashok-kumar-swain-2627a514
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0 | April 25, 2020 | Initial release
|
||||
|
||||
## Disclaimer
|
||||
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
- git clone the repo
|
||||
- npm i
|
||||
- npm i -g gulp
|
||||
- gulp serve
|
||||
|
||||
#### Local Mode
|
||||
A browser in local mode (localhost) will be opened.
|
||||
https://localhost:4321/temp/workbench.html
|
||||
|
||||
#### SharePoint Mode
|
||||
If you want to try on a real environment, open:
|
||||
https://your-domain.sharepoint.com/_layouts/15/workbench.aspx
|
||||
|
||||
## Usage
|
||||
- Create a FAQ List in SharePoint.
|
||||
- Create the below fields:
|
||||
- Title field can be considered as "Question" field.
|
||||
- Column Name Field Type
|
||||
- Title Single line of text
|
||||
- Answer Multiple lines of text
|
||||
- Category Single line of text
|
||||
- CategorySortOrder Number
|
||||
- QuestionSortOrder Number
|
||||
|
||||
|
||||
- Edit the webpart, set the the ListName in the property pane
|
||||
|
||||
## Features
|
||||
|
||||
- This Web Part allows users to create Frequently Asked Questions(Faq App) in modern and classic SharePoint pages.
|
||||
- An accordion layout is used to make it easy to browse through different questions.
|
||||
- Expand answers to your most frequent questions.
|
||||
- Include text, links, images in your answers.
|
||||
- A search bar to make your FAQ accordion searchable.
|
||||
- This webpart allows to search within questions and answers which are stored in a SharePoint FAQ list.
|
||||
- Sorting is enabled on both the category & Question
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-FAQApp" />
|
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"react-faq-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/reactFaq/ReactFaqWebPart.js",
|
||||
"manifest": "./src/webparts/reactFaq/ReactFaqWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"ReactFaqWebPartStrings": "lib/webparts/reactFaq/loc/{locale}.js"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "spfxwebparts",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "spfxwebparts-client-side-solution",
|
||||
"id": "f3b9b2b7-533c-42db-9a81-d3ea68e1e8b6",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"isDomainIsolated": false
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/spfxwebparts.sppkg"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
|
||||
"port": 4321,
|
||||
"https": true,
|
||||
"initialPage": "https://localhost:5432/workbench",
|
||||
"api": {
|
||||
"port": 5432,
|
||||
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const gulp = require('gulp');
|
||||
const build = require('@microsoft/sp-build-web');
|
||||
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
|
||||
|
||||
build.initialize(gulp);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "spfxwebparts",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.27",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.12.1",
|
||||
"@fortawesome/react-fontawesome": "^0.1.8",
|
||||
"@microsoft/sp-core-library": "1.8.2",
|
||||
"@microsoft/sp-lodash-subset": "1.8.2",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.8.2",
|
||||
"@microsoft/sp-property-pane": "1.8.2",
|
||||
"@microsoft/sp-webpart-base": "1.8.2",
|
||||
"@types/es6-promise": "0.0.33",
|
||||
"@types/react": "16.7.22",
|
||||
"@types/react-dom": "16.8.0",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"office-ui-fabric-react": "6.143.0",
|
||||
"react": "16.7.0",
|
||||
"react-accessible-accordion": "^3.0.1",
|
||||
"react-autosuggest": "^9.4.3",
|
||||
"react-dom": "16.7.0",
|
||||
"react-html-parser": "^2.0.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "16.7.22"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "1.8.2",
|
||||
"@microsoft/sp-tslint-rules": "1.8.2",
|
||||
"@microsoft/sp-module-interfaces": "1.8.2",
|
||||
"@microsoft/sp-webpart-workbench": "1.8.2",
|
||||
"@microsoft/rush-stack-compiler-2.9": "0.7.7",
|
||||
"gulp": "~3.9.1",
|
||||
"@types/chai": "3.4.34",
|
||||
"@types/mocha": "2.2.38",
|
||||
"ajv": "~5.2.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,12 @@
|
|||
export interface IFaqProp {
|
||||
Id?: Number;
|
||||
Title?: string;
|
||||
Answer?: string;
|
||||
BusinessCategory?: string;
|
||||
Category?: string;
|
||||
CategorySortOrder?: Number;
|
||||
QuestionSortOrder?: Number;
|
||||
IsFullRow?: string;
|
||||
expandRow?: Boolean;
|
||||
Modified?: Date;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
import {IFaqProp} from './IFaqProp';
|
||||
export interface IFaqServices {
|
||||
getFaq:(listName) => Promise<IFaqProp[]>;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export{IFaqProp} from './IFaqProp';
|
||||
export{IFaqServices} from './IFaqServices';
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
import { ServiceScope, ServiceKey } from '@microsoft/sp-core-library';
|
||||
import { PageContext } from '@microsoft/sp-page-context';
|
||||
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
|
||||
import { IFaqServices, IFaqProp} from '../interface';
|
||||
|
||||
export class FaqServices implements IFaqServices {
|
||||
|
||||
|
||||
public static readonly serviceKey: ServiceKey<IFaqServices> = ServiceKey.create<IFaqServices>('vrd:IFaqServices', FaqServices);
|
||||
private _spHttpClient: SPHttpClient;
|
||||
private _pageContext: PageContext;
|
||||
private _currentWebUrl: string;
|
||||
|
||||
constructor(serviceScope: ServiceScope) {
|
||||
serviceScope.whenFinished(() => {
|
||||
this._spHttpClient = serviceScope.consume(SPHttpClient.serviceKey);
|
||||
this._pageContext = serviceScope.consume(PageContext.serviceKey);
|
||||
this._currentWebUrl = this._pageContext.web.absoluteUrl;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
public getFaq(listName): Promise<IFaqProp[]> {
|
||||
return new Promise<IFaqProp[]>((resolve: any) => {
|
||||
var ParentDetails = this.getFaqs(listName);
|
||||
resolve(ParentDetails);
|
||||
});
|
||||
}
|
||||
|
||||
public async getMockFaq(): Promise<any> {
|
||||
var tempOrg = [{
|
||||
Id: 1,
|
||||
Title: "What is the HR Policy?",
|
||||
Answer: "There is no change in HR Policy",
|
||||
Category: "HR Policy",
|
||||
CategorySortOrder: 3,
|
||||
QuestionSortOrder: 3,
|
||||
Modified: '2020-03-27T11:07:21Z'
|
||||
},
|
||||
{
|
||||
Id: 2,
|
||||
Title: "What changes should I expect (or not) as an employee?",
|
||||
Answer: "For the immediate future, There is no change.",
|
||||
Category: "Top Questions",
|
||||
CategorySortOrder: 2,
|
||||
QuestionSortOrder: 2,
|
||||
Modified: '2020-03-27T11:07:21Z'
|
||||
},
|
||||
{
|
||||
Id: 3,
|
||||
Title: "What is the finance policy in the company?",
|
||||
Answer: "There is change in Finance Policy. ",
|
||||
Category: "Finance Policy",
|
||||
CategorySortOrder: 3,
|
||||
QuestionSortOrder: 1,
|
||||
Modified: '2020-03-27T11:07:21Z'
|
||||
}
|
||||
];
|
||||
return tempOrg;
|
||||
}
|
||||
|
||||
public async getFaqs(listName: string): Promise<IFaqProp[]> {
|
||||
try {
|
||||
const FaqProp:IFaqProp[] = [];
|
||||
let restUrl: string = this._currentWebUrl;
|
||||
restUrl += "/_api/web/lists/getbytitle('" + listName + "')/items?$select=Id,Title,Answer,Category,CategorySortOrder,QuestionSortOrder,Modified";
|
||||
return await this._spHttpClient.get(restUrl, SPHttpClient.configurations.v1,
|
||||
{
|
||||
headers: {
|
||||
"Accept": "application/json;odata=nometadata",
|
||||
"odata-version": "3.0"
|
||||
}
|
||||
})
|
||||
.then((response: SPHttpClientResponse) => {
|
||||
return response.json().then((responseFormatted: any) => {
|
||||
if (response.ok) {
|
||||
var collection = responseFormatted.value;
|
||||
for (var i = 0; i < collection.length; i++) {
|
||||
FaqProp.push({
|
||||
Id: collection[i].Id,
|
||||
Title: collection[i].Title,
|
||||
Answer: collection[i].Answer,
|
||||
Category: collection[i].Category,
|
||||
CategorySortOrder: collection[i].CategorySortOrder,
|
||||
QuestionSortOrder: collection[i].QuestionSortOrder,
|
||||
Modified: collection[i].Modified
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
throw new Error(response.text().toString());
|
||||
}
|
||||
|
||||
return FaqProp;
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
console.log("Service API Error - " + error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "a3c5e704-6f57-4f19-86ea-3a462c18780e",
|
||||
"alias": "ReactFaqWebPart",
|
||||
"componentType": "WebPart",
|
||||
|
||||
// The "*" signifies that the version should be taken from the package.json
|
||||
"version": "*",
|
||||
"manifestVersion": 2,
|
||||
|
||||
// If true, the component can only be installed on sites where Custom Script is allowed.
|
||||
// Components that allow authors to embed arbitrary script code should set this to true.
|
||||
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
|
||||
"requiresCustomScript": false,
|
||||
"supportedHosts": ["SharePointWebPart"],
|
||||
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||
"group": { "default": "Other" },
|
||||
"title": { "default": "react-faq" },
|
||||
"description": { "default": "react-faq description" },
|
||||
"officeFabricIconFontName": "Page",
|
||||
"properties": {
|
||||
"description": "react-faq"
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||
import {
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField
|
||||
} from '@microsoft/sp-property-pane';
|
||||
|
||||
import * as strings from 'ReactFaqWebPartStrings';
|
||||
import ReactFaq from './components/ReactFaq';
|
||||
import { IReactFaqProps } from './components/IReactFaqProps';
|
||||
|
||||
export interface IReactFaqWebPartProps {
|
||||
listName:string;
|
||||
}
|
||||
|
||||
export default class ReactFaqWebPart extends BaseClientSideWebPart<IReactFaqWebPartProps> {
|
||||
|
||||
public render(): void {
|
||||
const element: React.ReactElement<IReactFaqProps > = React.createElement(
|
||||
ReactFaq,
|
||||
{
|
||||
listName:this.properties.listName,
|
||||
ServiceScope: this.context.serviceScope
|
||||
}
|
||||
);
|
||||
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
protected onDispose(): void {
|
||||
ReactDom.unmountComponentAtNode(this.domElement);
|
||||
}
|
||||
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPaneDescription
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.BasicGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneTextField('listName', {
|
||||
label: strings.ListNameFieldLabel
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import * as React from 'react';
|
||||
class ErrorBoundary extends React.Component<any, any> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
hasError: false,
|
||||
error: null,
|
||||
info: null
|
||||
};
|
||||
}
|
||||
public componentDidCatch(error, info) {
|
||||
this.setState({
|
||||
hasError: true,
|
||||
error: error,
|
||||
info: info
|
||||
});
|
||||
}
|
||||
public render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div>
|
||||
<h1>Oops, something went wrong :(</h1>
|
||||
<p>The error: {this.state.error.toString()}</p>
|
||||
<p>Where it occurred: {this.state.info.componentStack}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
export default ErrorBoundary;
|
|
@ -0,0 +1,5 @@
|
|||
import { ServiceScope } from '@microsoft/sp-core-library';
|
||||
export interface IReactFaqProps {
|
||||
listName: string;
|
||||
ServiceScope: ServiceScope;
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
|
||||
|
||||
.reactFaq {
|
||||
.container {
|
||||
max-width: 700px;
|
||||
margin: 0px auto;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.row {
|
||||
@include ms-Grid-row;
|
||||
@include ms-fontColor-white;
|
||||
background-color: $ms-color-themeDark;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.column {
|
||||
@include ms-Grid-col;
|
||||
@include ms-lg10;
|
||||
@include ms-xl8;
|
||||
@include ms-xlPush2;
|
||||
@include ms-lgPush1;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include ms-font-xl;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.button {
|
||||
// Our button
|
||||
text-decoration: none;
|
||||
height: 32px;
|
||||
|
||||
// Primary Button
|
||||
min-width: 80px;
|
||||
background-color: $ms-color-themePrimary;
|
||||
border-color: $ms-color-themePrimary;
|
||||
color: $ms-color-white;
|
||||
|
||||
// Basic Button
|
||||
outline: transparent;
|
||||
position: relative;
|
||||
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: $ms-font-size-m;
|
||||
font-weight: $ms-font-weight-regular;
|
||||
border-width: 0;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
padding: 0 16px;
|
||||
|
||||
.label {
|
||||
font-weight: $ms-font-weight-semibold;
|
||||
font-size: $ms-font-size-m;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin: 0 4px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,633 @@
|
|||
import * as React from 'react';
|
||||
import { IReactFaqProps } from './IReactFaqProps';
|
||||
import { IFaqProp, IFaqServices } from '../../../interface';
|
||||
import { ServiceScope, ServiceKey, Environment, EnvironmentType } from '@microsoft/sp-core-library';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import * as fontawesome from '@fortawesome/free-solid-svg-icons';
|
||||
import Autosuggest from 'react-autosuggest';
|
||||
import { FaqServices } from '../../../services/FaqServices';
|
||||
import ReactHtmlParser from 'react-html-parser';
|
||||
import './reactAccordion.css';
|
||||
|
||||
|
||||
import {
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
AccordionItemHeading,
|
||||
AccordionItemButton,
|
||||
AccordionItemPanel,
|
||||
} from 'react-accessible-accordion';
|
||||
|
||||
|
||||
import './index.css';
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
|
||||
|
||||
|
||||
export interface IFaqState {
|
||||
|
||||
originalData: IFaqProp[];
|
||||
actualData: IFaqProp[];
|
||||
BusinessCategory: any;
|
||||
isLoading: Boolean;
|
||||
errorCause: String;
|
||||
selectedEntity: any;
|
||||
show: Boolean;
|
||||
filterData: any;
|
||||
searchValue: String;
|
||||
filteredCategoryData: any;
|
||||
filteredQuestion: String;
|
||||
value: String;
|
||||
suggestions: any;
|
||||
actualCanvasContentHeight: number;
|
||||
actualCanvasWrapperHeight: number;
|
||||
actualAccordionHeight: number;
|
||||
}
|
||||
|
||||
|
||||
export default class ReactFaq extends React.Component<IReactFaqProps, IFaqState> {
|
||||
|
||||
private faqServicesInstance: IFaqServices;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
originalData: [],
|
||||
actualData: [],
|
||||
BusinessCategory: [],
|
||||
isLoading: true,
|
||||
errorCause: "No Data",
|
||||
selectedEntity: [],
|
||||
show: false,
|
||||
filterData: [],
|
||||
searchValue: "",
|
||||
filteredCategoryData: [],
|
||||
filteredQuestion: '',
|
||||
value: '',
|
||||
suggestions: [],
|
||||
actualCanvasContentHeight: 0,
|
||||
actualCanvasWrapperHeight: 0,
|
||||
actualAccordionHeight: 0
|
||||
};
|
||||
try {
|
||||
let serviceScope: ServiceScope;
|
||||
serviceScope = this.props.ServiceScope;
|
||||
if (Environment.type == EnvironmentType.SharePoint || Environment.type == EnvironmentType.ClassicSharePoint) {
|
||||
// Mapping to be used when webpart runs in SharePoint.
|
||||
this.faqServicesInstance = serviceScope.consume(FaqServices.serviceKey);
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public onHandleChange = (event, value, FaqData) => {
|
||||
if (FaqData.length > 0 && event != undefined) {
|
||||
if (value == "") {
|
||||
const FaqFilteredData = this.filterByValue(FaqData, value);
|
||||
this.setState({ originalData: FaqFilteredData });
|
||||
}
|
||||
else {
|
||||
this.setState({ originalData: this.state.actualData });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public onChange = (event, { newValue }) => {
|
||||
if (newValue != "") {
|
||||
this.setState({
|
||||
value: newValue,
|
||||
});
|
||||
}
|
||||
else {
|
||||
|
||||
this.setState({
|
||||
originalData: this.state.actualData,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public onSuggestionSelected = (FaqData, event) => {
|
||||
const FaqFilteredData = this.filterByValue(FaqData, event.currentTarget.innerText);
|
||||
if (FaqFilteredData) {
|
||||
if (FaqFilteredData.length > 0) {
|
||||
var autoSuggestTextbox = document.getElementById("txtSearchBox") as HTMLTextAreaElement;
|
||||
autoSuggestTextbox.value = event.currentTarget.innerText;
|
||||
autoSuggestTextbox.blur();
|
||||
let FaqId = FaqFilteredData[0].Id;
|
||||
let FaqCategory = FaqFilteredData[0].Category;
|
||||
var catData = [];
|
||||
catData.push(FaqCategory);
|
||||
this.setState({ filteredCategoryData: catData });
|
||||
var nodElem = 'acc-' + FaqCategory;
|
||||
var node = document.getElementsByClassName(nodElem);
|
||||
var chNode = node[0].children[0].children[0].children[0];
|
||||
var newAttr = document.createAttribute('aria-expanded');
|
||||
newAttr.value = 'true';
|
||||
chNode.setAttributeNode(newAttr);
|
||||
node[0].children[0].children[1].removeAttribute('hidden');
|
||||
var FaqNode = this.getFaqElement(FaqId);
|
||||
var txtNode = document.getElementById("txtSearchBox");
|
||||
var FaqEle = FaqNode[0];
|
||||
var newAttrII = document.createAttribute('aria-expanded');
|
||||
newAttrII.value = 'true';
|
||||
FaqEle.setAttributeNode(newAttrII);
|
||||
FaqEle.nextSibling.style.display = 'block';
|
||||
FaqEle.nextSibling.removeAttribute('class');
|
||||
if (FaqEle.previousElementSibling.previousSibling.classList != undefined) {
|
||||
FaqEle.previousElementSibling.previousSibling.classList.add("hideDiv");
|
||||
}
|
||||
else {
|
||||
// IE11 does not implement classList on <svg>
|
||||
let appliedClasses = FaqEle.previousElementSibling.previousSibling.getAttribute("class") || "";
|
||||
appliedClasses = appliedClasses.split(" ").indexOf("hideDiv") == -1
|
||||
? appliedClasses + " hideDiv"
|
||||
: appliedClasses;
|
||||
FaqEle.previousElementSibling.previousSibling.setAttribute('class', appliedClasses);
|
||||
}
|
||||
if (FaqEle.previousElementSibling.classList != undefined) {
|
||||
FaqEle.previousElementSibling.classList.remove("hideDiv");
|
||||
}
|
||||
else {
|
||||
// IE11 does not implement classList on <svg>
|
||||
let appliedClassesII = FaqEle.previousElementSibling.getAttribute("class") || "";
|
||||
appliedClassesII = appliedClassesII.split(" ").indexOf("hideDiv") != -1
|
||||
? appliedClassesII.replace(" hideDiv", "")
|
||||
: appliedClassesII + " hideDiv";
|
||||
FaqEle.previousElementSibling.setAttribute('class', appliedClassesII);
|
||||
}
|
||||
|
||||
var txtSibEle = txtNode.nextElementSibling;
|
||||
txtSibEle.classList.remove("react-autosuggest__suggestions-container--open");
|
||||
FaqEle.scrollIntoView({ behavior: 'smooth' });
|
||||
|
||||
if (document.getElementsByClassName("mainContent") != undefined && document.getElementsByClassName("mainContent").length > 0) {
|
||||
this.setFaqWebPartHeightDynamic();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public onSuggestionsFetchRequested = ({ value }) => {
|
||||
this.setState({
|
||||
suggestions: this.getSuggestions(value)
|
||||
});
|
||||
}
|
||||
|
||||
public onSuggestionsClearRequested = () => {
|
||||
var autoSuggestTextbox = document.getElementById("txtSearchBox") as HTMLTextAreaElement;
|
||||
if(autoSuggestTextbox.value == ""){
|
||||
autoSuggestTextbox.value = "";
|
||||
this.setState({
|
||||
suggestions: [],
|
||||
value: ""
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// When suggestion is clicked, Autosuggest needs to populate the input
|
||||
// based on the clicked suggestion. Teach Autosuggest how to calculate the
|
||||
// input value for every given suggestion.
|
||||
public getSuggestionValue = (suggestion) => {
|
||||
if (suggestion.length < 0) {
|
||||
return "";
|
||||
}
|
||||
else {
|
||||
return suggestion.Title;
|
||||
}
|
||||
}
|
||||
|
||||
public getSuggestions = (value) => {
|
||||
const inputValue = value.trim().toLowerCase();
|
||||
const inputLength = inputValue.length;
|
||||
return inputLength === 0 ? [] : this.state.actualData.filter(lang =>
|
||||
(lang.Title.toLowerCase().indexOf(inputValue) !== -1) ||
|
||||
(lang.Answer.toLowerCase().indexOf(inputValue) !== -1)
|
||||
);
|
||||
}
|
||||
|
||||
public renderSuggestion = (suggestion) => {
|
||||
return (
|
||||
<div>
|
||||
{suggestion.Title}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public setNodeValues = () => {
|
||||
var SPCanvasFirstParent = (document.getElementsByClassName("mainContent") != undefined && document.getElementsByClassName("mainContent").length > 0) ? document.getElementsByClassName("SPCanvas")[0].parentElement.offsetHeight : 0;
|
||||
var SPCanvasSecondParent = (document.getElementsByClassName("mainContent") != undefined && document.getElementsByClassName("mainContent").length > 0) ? document.getElementsByClassName("SPCanvas")[0].parentElement.parentElement.offsetHeight : 0;
|
||||
this.setState({
|
||||
actualCanvasContentHeight: SPCanvasFirstParent,
|
||||
actualCanvasWrapperHeight: SPCanvasSecondParent
|
||||
}, this.dynamicHeight);
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
if (Environment.type == EnvironmentType.SharePoint || Environment.type == EnvironmentType.ClassicSharePoint) {
|
||||
this.loadFaq();
|
||||
}
|
||||
else {
|
||||
//await this.loadMockFaq();
|
||||
}
|
||||
this.setState({
|
||||
actualAccordionHeight: (document.getElementsByClassName("accordion") != undefined && document.getElementsByClassName("accordion").length > 0) ? document.getElementsByClassName("accordion")[0].parentElement.offsetHeight : 0
|
||||
});
|
||||
var ua = window.navigator.userAgent;
|
||||
var trident = ua.indexOf('Trident/');
|
||||
|
||||
if (trident > 0) {
|
||||
// IE 11 => return version number
|
||||
var rv = ua.indexOf('rv:');
|
||||
if ((parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10)) < 12) {
|
||||
document.getElementById("txtSearchBox").style.paddingTop = '3px';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async loadFaq() {
|
||||
await this.faqServicesInstance.getFaq(this.props.listName).then((FaqData: IFaqProp[]) => {
|
||||
try {
|
||||
this.setState(
|
||||
{
|
||||
actualData: FaqData,
|
||||
originalData: FaqData
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
console.log("Error Occurred :" + error);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public categoryAndQuestionSorting = (Data) => {
|
||||
var result = [];
|
||||
// Get Distinct category for sorting Category
|
||||
var distCate = this.distinct(Data, "Category");
|
||||
distCate.sort((c, d) => {
|
||||
return c.CategorySortOrder - d.CategorySortOrder;
|
||||
});
|
||||
|
||||
//Sorting the FQA as per CategorySortOrder
|
||||
distCate.forEach((distCateItem) => {
|
||||
Data.map((item) => {
|
||||
if (distCateItem.Category.toLowerCase() == item.Category.toLowerCase()) {
|
||||
result.push(item);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//Sorting the FQA as per QuestionSortOrder
|
||||
result.sort((a, b) => {
|
||||
return a.QuestionSortOrder - b.QuestionSortOrder;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public distinct(items, prop) {
|
||||
var unique = [];
|
||||
var distinctItems = [];
|
||||
for (const item of items) {
|
||||
if (unique[item[prop]] === undefined) {
|
||||
distinctItems.push(item);
|
||||
}
|
||||
|
||||
unique[item[prop]] = 0;
|
||||
}
|
||||
return distinctItems;
|
||||
}
|
||||
|
||||
public filterByValue = (arrayData, value) => {
|
||||
return arrayData.filter(o =>
|
||||
this.includes(o["Title"].toLowerCase(), value.toLowerCase()) || this.includes(o["Answer"].toLowerCase(), value.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
public getFaqElement = (FaqId) => {
|
||||
return Array.prototype.filter.call(
|
||||
document.getElementsByTagName('span'),
|
||||
(el) => el.getAttribute('data-id') == FaqId
|
||||
);
|
||||
}
|
||||
|
||||
public formatDate = (ModifiedDate) => {
|
||||
const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
const dt = new Date(ModifiedDate);
|
||||
var hours = dt.getHours();
|
||||
var minutes = dt.getMinutes();
|
||||
var secs = dt.getSeconds();
|
||||
var ampm = hours >= 12 ? 'PM' : 'AM';
|
||||
hours = hours % 12;
|
||||
hours = hours ? hours : 12; // the hour '0' should be '12'
|
||||
var strTime = hours + ':' + minutes + ':' + secs + ' ' + ampm;
|
||||
|
||||
return monthNames[dt.getMonth()] + " " + dt.getDate() + ", " + dt.getFullYear() + " " + strTime;
|
||||
}
|
||||
|
||||
|
||||
public loadMoreEvent(event: any): void {
|
||||
var clickedId = event.target.getAttribute('data-id');
|
||||
console.log('clicked - ' + clickedId + ' ' + event.target);
|
||||
if (event.target.nodeName == "SPAN") {
|
||||
if (event.target.nextElementSibling.classList.contains("hideDiv")) {
|
||||
event.target.nextElementSibling.classList.remove("hideDiv");
|
||||
try {
|
||||
if (event.currentTarget.children[0].classList != undefined) {
|
||||
event.currentTarget.children[0].classList.add("hideDiv");
|
||||
}
|
||||
else {
|
||||
// IE11 does not implement classList on <svg>
|
||||
let appliedClasses = event.currentTarget.children[0].getAttribute("class") || "";
|
||||
appliedClasses = appliedClasses.split(" ").indexOf("hideDiv") == -1
|
||||
? appliedClasses + " hideDiv"
|
||||
: appliedClasses;
|
||||
event.currentTarget.children[0].setAttribute('class', appliedClasses);
|
||||
}
|
||||
|
||||
if (event.currentTarget.children[1].classList != undefined) {
|
||||
event.currentTarget.children[1].classList.remove("hideDiv");
|
||||
}
|
||||
else {
|
||||
// IE11 does not implement classList on <svg>
|
||||
let appliedClassesII = event.currentTarget.children[1].getAttribute("class") || "";
|
||||
appliedClassesII = appliedClassesII.split(" ").indexOf("hideDiv") != -1
|
||||
? appliedClassesII.replace(" hideDiv", "")
|
||||
: appliedClassesII + " hideDiv";
|
||||
event.currentTarget.children[1].setAttribute('class', appliedClassesII);
|
||||
}
|
||||
event.currentTarget.children[3].removeAttribute("style");
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
else {
|
||||
event.target.nextElementSibling.classList.add("hideDiv");
|
||||
try {
|
||||
if (event.currentTarget.children[1].classList != undefined) {
|
||||
event.currentTarget.children[1].classList.add("hideDiv");
|
||||
}
|
||||
else {
|
||||
// IE11 does not implement classList on <svg>
|
||||
let appliedClasses = event.currentTarget.children[1].getAttribute("class") || "";
|
||||
appliedClasses = appliedClasses.split(" ").indexOf("hideDiv") == -1
|
||||
? appliedClasses + " hideDiv"
|
||||
: appliedClasses;
|
||||
event.currentTarget.children[1].setAttribute('class', appliedClasses);
|
||||
}
|
||||
|
||||
if (event.currentTarget.children[0].classList != undefined) {
|
||||
event.currentTarget.children[0].classList.remove("hideDiv");
|
||||
}
|
||||
else {
|
||||
// IE11 does not implement classList on <svg>
|
||||
let appliedClassesII = event.currentTarget.children[0].getAttribute("class") || "";
|
||||
appliedClassesII = appliedClassesII.split(" ").indexOf("hideDiv") != -1
|
||||
? appliedClassesII.replace(" hideDiv", "")
|
||||
: appliedClassesII + " hideDiv";
|
||||
event.currentTarget.children[0].setAttribute('class', appliedClassesII);
|
||||
}
|
||||
event.currentTarget.children[3].removeAttribute("style");
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (event.target.nodeName == "path") {
|
||||
if (event.currentTarget.children[1] != undefined) {
|
||||
event.currentTarget.children[1].classList.add("hideDiv");
|
||||
event.currentTarget.children[0].classList.add("hideDiv");
|
||||
}
|
||||
else {
|
||||
// IE11 does not implement classList on <svg>
|
||||
let appliedClasses = event.currentTarget.children[0].getAttribute("class") || "";
|
||||
appliedClasses = appliedClasses + " hideDiv";
|
||||
event.currentTarget.children[0].setAttribute('class', appliedClasses);
|
||||
let appliedClassesII = event.currentTarget.children[1].getAttribute("class") || "";
|
||||
appliedClassesII = appliedClassesII + " hideDiv";
|
||||
event.currentTarget.children[1].setAttribute('class', appliedClassesII);
|
||||
}
|
||||
if (event.target.parentElement.getAttribute('data-icon') == "plus-square") {
|
||||
event.target.parentElement.nextElementSibling.nextElementSibling.nextElementSibling.classList.remove("hideDiv");
|
||||
|
||||
if (event.currentTarget.children[1].classList != undefined) {
|
||||
event.currentTarget.children[1].classList.remove("hideDiv");
|
||||
}
|
||||
else {
|
||||
// IE11 does not implement classList on <svg>
|
||||
let appliedClassesII = event.currentTarget.children[1].getAttribute("class") || "";
|
||||
appliedClassesII = appliedClassesII.split(" ").indexOf("hideDiv") != -1
|
||||
? appliedClassesII.replace(" hideDiv", "")
|
||||
: appliedClassesII + " hideDiv";
|
||||
event.currentTarget.children[1].setAttribute('class', appliedClassesII);
|
||||
}
|
||||
}
|
||||
else {
|
||||
event.target.parentElement.nextElementSibling.nextElementSibling.classList.add("hideDiv");
|
||||
event.target.parentElement.nextElementSibling.nextElementSibling.removeAttribute("style");
|
||||
if (event.currentTarget.children[0].classList != undefined) {
|
||||
event.currentTarget.children[0].classList.remove("hideDiv");
|
||||
}
|
||||
else {
|
||||
// IE11 does not implement classList on <svg>
|
||||
let appliedClassesII = event.currentTarget.children[0].getAttribute("class") || "";
|
||||
appliedClassesII = appliedClassesII.split(" ").indexOf("hideDiv") != -1
|
||||
? appliedClassesII.replace(" hideDiv", "")
|
||||
: appliedClassesII + " hideDiv";
|
||||
event.currentTarget.children[0].setAttribute('class', appliedClassesII);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (event.target.nodeName == "svg") {
|
||||
if (event.target.classList != undefined) {
|
||||
event.target.classList.add("hideDiv");
|
||||
}
|
||||
else {
|
||||
// IE11 does not implement classList on <svg>
|
||||
let appliedClasses = event.target.getAttribute("class") || "";
|
||||
appliedClasses = appliedClasses + " hideDiv";
|
||||
event.target.setAttribute('class', appliedClasses);
|
||||
}
|
||||
//alert('path');
|
||||
if (event.target.getAttribute('data-icon') == "plus-square") {
|
||||
event.target.nextElementSibling.nextElementSibling.nextElementSibling.classList.remove("hideDiv");
|
||||
//event.target.nextElementSibling.classList.remove("hideDiv");
|
||||
|
||||
if (event.target.nextElementSibling.classList != undefined) {
|
||||
event.target.nextElementSibling.classList.remove("hideDiv");
|
||||
}
|
||||
else {
|
||||
// IE11 does not implement classList on <svg>
|
||||
let appliedClassesII = event.target.nextElementSibling.getAttribute("class") || "";
|
||||
appliedClassesII = appliedClassesII.split(" ").indexOf("hideDiv") != -1
|
||||
? appliedClassesII.replace(" hideDiv", "")
|
||||
: appliedClassesII + " hideDiv";
|
||||
event.target.nextElementSibling.setAttribute('class', appliedClassesII);
|
||||
}
|
||||
}
|
||||
else {
|
||||
event.target.nextElementSibling.nextElementSibling.classList.add("hideDiv");
|
||||
event.target.nextElementSibling.nextElementSibling.removeAttribute("style");
|
||||
if (event.target.previousElementSibling.classList != undefined) {
|
||||
event.target.previousElementSibling.classList.remove("hideDiv");
|
||||
}
|
||||
else {
|
||||
// IE11 does not implement classList on <svg>
|
||||
let appliedClassesII = event.target.previousElementSibling.getAttribute("class") || "";
|
||||
appliedClassesII = appliedClassesII.split(" ").indexOf("hideDiv") != -1
|
||||
? appliedClassesII.replace(" hideDiv", "")
|
||||
: appliedClassesII + " hideDiv";
|
||||
event.target.previousElementSibling.setAttribute('class', appliedClassesII);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
if (event.target.getAttribute('data-icon') == "plus-square") {
|
||||
event.target.nextElementSibling.nextElementSibling.nextElementSibling.classList.remove("hideDiv");
|
||||
event.target.nextElementSibling.classList.remove("hideDiv");
|
||||
event.target.classList.add("hideDiv");
|
||||
}
|
||||
else {
|
||||
event.target.nextElementSibling.nextElementSibling.classList.add("hideDiv");
|
||||
event.target.previousElementSibling.classList.add("hideDiv");
|
||||
event.target.classList.add("hideDiv");
|
||||
event.target.removeAttribute("style");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (document.getElementsByClassName("mainContent") != undefined && document.getElementsByClassName("mainContent").length > 0) {
|
||||
this.setFaqWebPartHeightDynamic();
|
||||
}
|
||||
}
|
||||
|
||||
public dynamicHeight = () => {
|
||||
var SPCanvasNode = document.getElementsByClassName("SPCanvas");
|
||||
var accordionNode = document.getElementsByClassName("accordion");
|
||||
if (SPCanvasNode.length > 0 && accordionNode.length > 0) {
|
||||
SPCanvasNode[0].parentElement.style.height = (this.state.actualCanvasContentHeight + (accordionNode[0].parentElement.offsetHeight - this.state.actualAccordionHeight)) + "px";
|
||||
SPCanvasNode[0].parentElement.parentElement.style.height = (this.state.actualCanvasWrapperHeight + (accordionNode[0].parentElement.offsetHeight - this.state.actualAccordionHeight)) + "px";
|
||||
}
|
||||
}
|
||||
|
||||
public setFaqWebPartHeightDynamic = () => {
|
||||
if (this.state.actualCanvasContentHeight == 0) {
|
||||
this.setNodeValues();
|
||||
}
|
||||
else {
|
||||
this.dynamicHeight();
|
||||
}
|
||||
}
|
||||
|
||||
public accordionOnchange = () => {
|
||||
if (document.getElementsByClassName("mainContent") != undefined && document.getElementsByClassName("mainContent").length > 0) {
|
||||
this.setFaqWebPartHeightDynamic();
|
||||
}
|
||||
}
|
||||
|
||||
public includes = (container, value) => {
|
||||
var returnValue = false;
|
||||
var pos = container.indexOf(value);
|
||||
if (pos >= 0) {
|
||||
returnValue = true;
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IReactFaqProps> {
|
||||
var uniqueBC = [];
|
||||
var FaqData = [];
|
||||
|
||||
if (this.state.originalData.length > 0) {
|
||||
FaqData = this.categoryAndQuestionSorting(this.state.originalData);
|
||||
uniqueBC = this.distinct(FaqData, "BusinessCategory");
|
||||
}
|
||||
|
||||
const { value, suggestions } = this.state;
|
||||
|
||||
// Autosuggest will pass through all these props to the input.
|
||||
const inputProps = {
|
||||
placeholder: 'Search existing Faq',
|
||||
value,
|
||||
onChange: this.onChange,
|
||||
id: 'txtSearchBox'
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`container`}>
|
||||
|
||||
<div className="FaqSearchBox" accept-charset="UTF-8">
|
||||
<Autosuggest
|
||||
suggestions={suggestions}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
getSuggestionValue={this.getSuggestionValue}
|
||||
renderSuggestion={this.renderSuggestion}
|
||||
onSuggestionSelected={this.onSuggestionSelected.bind(this, this.state.actualData)}
|
||||
inputProps={inputProps}
|
||||
focusInputOnSuggestionClick={false}
|
||||
|
||||
/>
|
||||
</div>
|
||||
<ErrorBoundary>
|
||||
|
||||
<div className="clearBody">
|
||||
|
||||
<Accordion allowMultipleExpanded={true} allowZeroExpanded={true} onChange={this.accordionOnchange.bind(this)} preExpanded={this.state.filteredCategoryData}
|
||||
>
|
||||
{uniqueBC.map((item) => (
|
||||
<div>
|
||||
{this.distinct(FaqData, "Category").map((allCat) => (
|
||||
<div className={`acc-${allCat.Category}`}>
|
||||
<AccordionItem uuid={allCat.Category}>
|
||||
<AccordionItemHeading>
|
||||
<AccordionItemButton >
|
||||
{allCat.Category}
|
||||
</AccordionItemButton>
|
||||
</AccordionItemHeading>
|
||||
<AccordionItemPanel>
|
||||
<div className="acc-item-panel">
|
||||
{FaqData.filter(it => it.Category == allCat.Category).map((allFaq) => (
|
||||
|
||||
<div
|
||||
className="acc-item"
|
||||
data-id={allFaq.Id}
|
||||
onClick={
|
||||
event => this.loadMoreEvent(event)
|
||||
}>
|
||||
<FontAwesomeIcon icon={fontawesome.faPlusSquare} size="1x" data-id={allFaq.Id} className={"plusminusImg"} />
|
||||
<FontAwesomeIcon icon={fontawesome.faMinusSquare} size="1x" data-id={allFaq.Id} className={"plusminusImg hideDiv"} />
|
||||
<span className="acc-span-text" data-id={allFaq.Id}>{allFaq.Title}</span>
|
||||
<div className="hideDiv">
|
||||
<span className="acc-modified-text">Last Modified : {this.formatDate(allFaq.Modified)}</span>
|
||||
<div className="acc-answer">
|
||||
{ReactHtmlParser(allFaq.Answer)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
))}
|
||||
</div>
|
||||
</AccordionItemPanel>
|
||||
</AccordionItem>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</Accordion>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
.headerSection {
|
||||
background-color: #E20074;
|
||||
color: #fff;
|
||||
padding: 10px 10px 20px 10px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.fullrow {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.faqTitle {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.faqQuestionsList span {
|
||||
color: #E20074;
|
||||
display: block;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.col3row {
|
||||
width: 33.33%;
|
||||
float: left;
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.bodySection {
|
||||
float: left;
|
||||
width: 100%;
|
||||
border: 1px solid #ccc;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.clearBody{
|
||||
float: left;
|
||||
width: 100%;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
.imgSec{
|
||||
float: left;
|
||||
padding-right: 10px;
|
||||
|
||||
}
|
||||
|
||||
.acc-item{
|
||||
float: left;
|
||||
color: #e20074;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.plusminusImg{
|
||||
padding-right: 10px;
|
||||
font-size: 28px;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.hideDiv{
|
||||
display: none;
|
||||
}
|
||||
.acc-answer{
|
||||
color: #999;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.accordion__panel{
|
||||
width: 100%;
|
||||
border: 1px solid #e9e9e9;
|
||||
margin-top: -15px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
|
||||
.titlefullrow{
|
||||
margin: 10px 20px;
|
||||
border: 1px solid #e20074;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.titlefullrow:hover{
|
||||
box-shadow: inset 0 0 5px #e20074;
|
||||
transition: box-shadow 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.titlecol3row {
|
||||
width: calc(33.33% - 40px);
|
||||
float: left;
|
||||
border: 1px solid #e20074;
|
||||
box-sizing: border-box;
|
||||
margin: 10px 20px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.titlecol3row:hover{
|
||||
box-shadow: inset 0 0 5px #e20074;
|
||||
transition: box-shadow 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.titlefaqTitle{
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
color: #5c5c5c;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.titlecol3row:hover .titlefaqTitle, .titlefullrow:hover .titlefaqTitle{
|
||||
color: #E20074;
|
||||
}
|
||||
|
||||
.acc-item-panel{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.accordion__button {
|
||||
background-color: #fff;
|
||||
color: #5c5c5c !important;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
margin-bottom: 15px;
|
||||
font-weight: 600;
|
||||
border: 1px solid #e20074;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.accordion{
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.titleView{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.viewAllQuestions{
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
text-decoration: underline;
|
||||
color: #e20074;
|
||||
}
|
||||
|
||||
.acc-span-text{
|
||||
color: #5c5c5c!important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.acc-modified-text{
|
||||
font-family: 'Tele-GroteskFet';
|
||||
font-size: 14px;
|
||||
color: rgb(102, 102, 102) !important;
|
||||
}
|
||||
|
||||
|
||||
.accordion__button{
|
||||
font-family: 'Tele-GroteskFet';
|
||||
font-size: 20px;
|
||||
color: #000;
|
||||
/* text-transform: capitalize; */
|
||||
}
|
||||
|
||||
.acc-span-text{
|
||||
font-family: 'Tele-GroteskHal';
|
||||
font-size: 20px;
|
||||
color: #000;
|
||||
/* text-transform: capitalize; */
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.acc-answer{
|
||||
font-family: 'Tele-GroteskHal' !important;
|
||||
font-size: 18px !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
.d-none{
|
||||
display: none;
|
||||
}
|
||||
.react-autosuggest__container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.react-autosuggest__input {
|
||||
width: 80%;
|
||||
height: 40px;
|
||||
padding: 10px 20px;
|
||||
font-family: 'Tele-GroteskHal' !important;
|
||||
font-weight: 300;
|
||||
font-size: 20px;
|
||||
border: 1px solid #e20074;
|
||||
border-radius: 30px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.react-autosuggest__input::before{
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background-color: #fff;
|
||||
color: #ddd;
|
||||
content: "\1F50D";
|
||||
font-size: 18px;
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
}
|
||||
|
||||
.react-autosuggest__input--focused {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.react-autosuggest__suggestions-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.react-autosuggest__suggestions-container--open {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 51px;
|
||||
width: 80%;
|
||||
border: 1px solid #aaa;
|
||||
background-color: #fff;
|
||||
font-family: Helvetica, sans-serif;
|
||||
font-weight: 300;
|
||||
font-size: 16px;
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
z-index: 2;
|
||||
box-shadow: 10px 0 5px 0px #888;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
}
|
||||
|
||||
.react-autosuggest__suggestions-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.react-autosuggest__suggestion {
|
||||
cursor: pointer;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.react-autosuggest__suggestion--highlighted {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.react-autosuggest__section-container {
|
||||
border-top: 1px dashed #ccc;
|
||||
}
|
||||
|
||||
.react-autosuggest__section-container--first {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.react-autosuggest__section-title {
|
||||
padding: 10px 0 0 10px;
|
||||
font-size: 12px;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.faqSearchBox{
|
||||
margin: 20px 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.acc-answer a {
|
||||
color: #e20074 !important;
|
||||
}
|
||||
|
||||
.acc-answer a span{
|
||||
color: #e20074 !important;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.accordion {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.accordion__item + .accordion__item {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.accordion__button {
|
||||
background-color: #fff !important;
|
||||
color: #5c5c5c !important;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
margin-bottom: 15px;
|
||||
font-weight: 600;
|
||||
border: 1px solid #e20074 !important;
|
||||
border-radius: 4px;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.accordion__button:hover {
|
||||
background-color: #ddd;
|
||||
border-color: #e20074 !important;
|
||||
border-width: 2px !important;
|
||||
}
|
||||
|
||||
.accordion__button:before {
|
||||
display: inline-block;
|
||||
content: '';
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
margin-right: 12px;
|
||||
border-bottom: 2px solid #e20074;
|
||||
border-right: 2px solid #e20074;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.accordion__button[aria-expanded='true']::before,
|
||||
.accordion__button[aria-selected='true']::before {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.accordion__panel {
|
||||
padding: 20px;
|
||||
animation: fadein 0.35s ease-in;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Description",
|
||||
"BasicGroupName": "Group Name",
|
||||
"DescriptionFieldLabel": "Description Field",
|
||||
"ListNameFieldLabel": "List Name"
|
||||
}
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
declare interface IReactFaqWebPartStrings {
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
DescriptionFieldLabel: string;
|
||||
ListNameFieldLabel: string;
|
||||
|
||||
}
|
||||
|
||||
declare module 'ReactFaqWebPartStrings' {
|
||||
const strings: IReactFaqWebPartStrings;
|
||||
export = strings;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-2.9/includes/tsconfig-web.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "lib",
|
||||
"inlineSources": false,
|
||||
"strictNullChecks": false,
|
||||
"noUnusedLocals": false,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"lib"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
|
||||
"rules": {
|
||||
"class-name": false,
|
||||
"export-name": false,
|
||||
"forin": false,
|
||||
"label-position": false,
|
||||
"member-access": true,
|
||||
"no-arg": false,
|
||||
"no-console": false,
|
||||
"no-construct": false,
|
||||
"no-duplicate-variable": true,
|
||||
"no-eval": false,
|
||||
"no-function-expression": true,
|
||||
"no-internal-module": true,
|
||||
"no-shadowed-variable": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-unnecessary-semicolons": true,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-with-statement": true,
|
||||
"semicolon": true,
|
||||
"trailing-comma": false,
|
||||
"typedef": false,
|
||||
"typedef-whitespace": false,
|
||||
"use-named-parameter": true,
|
||||
"variable-name": false,
|
||||
"whitespace": false
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue