From dd81769b57f1bf64ac79763867440de9db317e78 Mon Sep 17 00:00:00 2001 From: Ina Glushkova Date: Wed, 21 May 2025 11:38:19 +0300 Subject: [PATCH 01/11] chore: add kendo-react-grid-performance app --- .../kendo-react-grid-performance/.gitignore | 27 ++ .../kendo-react-grid-performance/README.md | 9 + .../kendo-react-grid-performance/package.json | 60 +++ .../public/favicon.ico | Bin 0 -> 3870 bytes .../public/index.html | 44 ++ .../public/logo192.png | Bin 0 -> 5347 bytes .../public/logo512.png | Bin 0 -> 9664 bytes .../public/manifest.json | 25 ++ .../public/robots.txt | 3 + .../kendo-react-grid-performance/src/App.css | 129 ++++++ .../kendo-react-grid-performance/src/App.js | 19 + .../src/App.test.js | 8 + .../src/DataService/index.jsx | 123 +++++ .../DataService/local-data/financial-data.js | 422 ++++++++++++++++++ .../src/DataService/local-data/trends.js | 23 + .../src/KendoGrid/column-menu.jsx | 14 + .../src/KendoGrid/index.jsx | 415 +++++++++++++++++ .../src/KendoGrid/templates.jsx | 56 +++ .../src/index.css | 13 + .../kendo-react-grid-performance/src/index.js | 17 + .../kendo-react-grid-performance/src/logo.svg | 1 + .../src/reportWebVitals.js | 13 + .../src/setupTests.js | 5 + .../src/utils/index.js | 25 ++ .../tsconfig.json | 24 + 25 files changed, 1475 insertions(+) create mode 100644 examples/kendo-react-grid-performance/.gitignore create mode 100644 examples/kendo-react-grid-performance/README.md create mode 100644 examples/kendo-react-grid-performance/package.json create mode 100644 examples/kendo-react-grid-performance/public/favicon.ico create mode 100644 examples/kendo-react-grid-performance/public/index.html create mode 100644 examples/kendo-react-grid-performance/public/logo192.png create mode 100644 examples/kendo-react-grid-performance/public/logo512.png create mode 100644 examples/kendo-react-grid-performance/public/manifest.json create mode 100644 examples/kendo-react-grid-performance/public/robots.txt create mode 100644 examples/kendo-react-grid-performance/src/App.css create mode 100644 examples/kendo-react-grid-performance/src/App.js create mode 100644 examples/kendo-react-grid-performance/src/App.test.js create mode 100644 examples/kendo-react-grid-performance/src/DataService/index.jsx create mode 100644 examples/kendo-react-grid-performance/src/DataService/local-data/financial-data.js create mode 100644 examples/kendo-react-grid-performance/src/DataService/local-data/trends.js create mode 100644 examples/kendo-react-grid-performance/src/KendoGrid/column-menu.jsx create mode 100644 examples/kendo-react-grid-performance/src/KendoGrid/index.jsx create mode 100644 examples/kendo-react-grid-performance/src/KendoGrid/templates.jsx create mode 100644 examples/kendo-react-grid-performance/src/index.css create mode 100644 examples/kendo-react-grid-performance/src/index.js create mode 100644 examples/kendo-react-grid-performance/src/logo.svg create mode 100644 examples/kendo-react-grid-performance/src/reportWebVitals.js create mode 100644 examples/kendo-react-grid-performance/src/setupTests.js create mode 100644 examples/kendo-react-grid-performance/src/utils/index.js create mode 100644 examples/kendo-react-grid-performance/tsconfig.json diff --git a/examples/kendo-react-grid-performance/.gitignore b/examples/kendo-react-grid-performance/.gitignore new file mode 100644 index 00000000..aae8560f --- /dev/null +++ b/examples/kendo-react-grid-performance/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local +**/kendo-ui-license** + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# package-lock file +package-lock.json \ No newline at end of file diff --git a/examples/kendo-react-grid-performance/README.md b/examples/kendo-react-grid-performance/README.md new file mode 100644 index 00000000..df0ed2f7 --- /dev/null +++ b/examples/kendo-react-grid-performance/README.md @@ -0,0 +1,9 @@ +The start the application locally follow these steps: + +1. Clone or download the source code from GitHub. +1. Install the required dependencies using `npm install --save`. +1. Check and activate your license - https://www.telerik.com/kendo-react-ui/components/my-license/ +1. Run the application using `npm start`. +1. To build it for production with the profiler active using the command `npm run build -- --profile`. +1. Then serve the create production build with `serve -s build`. +1. Observe the real performance results from the production build. diff --git a/examples/kendo-react-grid-performance/package.json b/examples/kendo-react-grid-performance/package.json new file mode 100644 index 00000000..a4e7afa3 --- /dev/null +++ b/examples/kendo-react-grid-performance/package.json @@ -0,0 +1,60 @@ +{ + "name": "kendo-react-grid-performance", + "version": "0.1.0", + "private": true, + "dependencies": { + "@progress/kendo-react-grid": "7.1.0", + "@progress/kendo-data-query": "1.7.0", + "@progress/kendo-drawing": "1.19.0", + "@progress/kendo-licensing": "1.3.5", + "@progress/kendo-svg-icons": "2.1.0", + "@progress/kendo-react-common": "7.1.0", + "@progress/kendo-react-animation": "7.1.0", + "@progress/kendo-popup-common": "1.9.2", + "@progress/kendo-react-popup": "7.1.0", + "@progress/kendo-react-buttons": "7.1.0", + "@progress/kendo-date-math": "1.5.12", + "@progress/kendo-intl": "3.1.2", + "@progress/kendo-react-intl": "7.1.0", + "@progress/kendo-react-labels": "7.1.0", + "@progress/kendo-react-progressbars": "7.1.0", + "@progress/kendo-react-layout": "7.1.0", + "@progress/kendo-react-dateinputs": "7.1.0", + "@progress/kendo-inputs-common": "3.1.0", + "@progress/kendo-react-dialogs": "7.1.0", + "@progress/kendo-react-form": "7.1.0", + "@progress/kendo-react-inputs": "7.1.0", + "@progress/kendo-react-treeview": "7.1.0", + "@progress/kendo-react-dropdowns": "7.1.0", + "@progress/kendo-react-data-tools": "7.1.0", + "@progress/kendo-theme-default": "^7.2.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-scripts": "^5.0.1", + "web-vitals": "^3.4.0" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/examples/kendo-react-grid-performance/public/favicon.ico b/examples/kendo-react-grid-performance/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a GIT binary patch literal 3870 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ literal 0 HcmV?d00001 diff --git a/examples/kendo-react-grid-performance/public/index.html b/examples/kendo-react-grid-performance/public/index.html new file mode 100644 index 00000000..e0a8b497 --- /dev/null +++ b/examples/kendo-react-grid-performance/public/index.html @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/examples/kendo-react-grid-performance/public/logo192.png b/examples/kendo-react-grid-performance/public/logo192.png new file mode 100644 index 0000000000000000000000000000000000000000..fc44b0a3796c0e0a64c3d858ca038bd4570465d9 GIT binary patch literal 5347 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 b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN literal 0 HcmV?d00001 diff --git a/examples/kendo-react-grid-performance/public/manifest.json b/examples/kendo-react-grid-performance/public/manifest.json new file mode 100644 index 00000000..080d6c77 --- /dev/null +++ b/examples/kendo-react-grid-performance/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/examples/kendo-react-grid-performance/public/robots.txt b/examples/kendo-react-grid-performance/public/robots.txt new file mode 100644 index 00000000..e9e57dc4 --- /dev/null +++ b/examples/kendo-react-grid-performance/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/examples/kendo-react-grid-performance/src/App.css b/examples/kendo-react-grid-performance/src/App.css new file mode 100644 index 00000000..9dc2d83f --- /dev/null +++ b/examples/kendo-react-grid-performance/src/App.css @@ -0,0 +1,129 @@ +/* You can add global styles to this file, and also import other style files */ +body { + margin: 0; + font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #212529; + text-align: left; + background-color: #fff; +} + +.k-grid-table td { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.k-grid-table td.numeric { + text-align: right; +} + +.k-grid-table .fintech-icons .k-icon { + flex: 0 1 auto; + font-size: 16px; + width: 16px; + height: 16px; + margin-left: 4px; +} + +.k-grid-table .fintech-icons { + display: flex; + justify-content: flex-end; +} + +.k-grid-table .changePositive, +.k-grid-table .changeNegative, +.k-grid-table .strongPositive, +.k-grid-table .strongNegative { + color: #fff !important; + padding: 2px 5px; +} + +.k-grid-table .positive { + color: #4eb862 !important; +} + +.k-grid-table .positive.strongPositive { + color: rgba(78, 184, 98, 0.8) !important; +} + +.k-grid-table .negative { + color: #d31642 !important; +} + +.k-grid-table .negative.strongNegative { + color: rgba(255, 19, 74, 0.8) !important; +} + +.k-grid-table .strongNegative { + color: #fff; +} + +.k-grid-table .change .changePositive { + border-right: 4px solid #335e3b; + padding-right: 4px; +} + +.k-grid-table .change .changeNegative { + border-right: 4px solid #7a1c32; + padding-right: 4px; +} + +.k-grid-table .change .strongPositive { + border-right: 4px solid #459a55; + padding-right: 4px; +} + +.k-grid-table .change .strongNegative { + border-right: 4px solid #d31642; + padding-right: 4px; +} + +.controls-holder { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; +} + +.switches { + display: flex; + justify-content: space-between; + align-items: center; + flex: 1 0 0%; + padding-right: 20px; + font-size: 0.9rem; + margin-bottom: -20px; +} + +.control-item { + padding-right: 20px; +} + +.k-slider { + height: 24px; + display: block; +} + +.fintech-slider { + width: 40%; + min-width: 145px; +} + +.fintech-play-controls { + width: 45%; + min-width: 500px; + margin-top: 20px; +} + +.fintech-play-controls button { + width: 200px; +} + +.sample-toolbar { + height: 20px; + font-size: 0.8rem; + line-height: 20px; +} \ No newline at end of file diff --git a/examples/kendo-react-grid-performance/src/App.js b/examples/kendo-react-grid-performance/src/App.js new file mode 100644 index 00000000..9d349b8a --- /dev/null +++ b/examples/kendo-react-grid-performance/src/App.js @@ -0,0 +1,19 @@ +import React from 'react'; +import './App.css'; +import { KendoGrid } from './KendoGrid'; +import { DataProvider } from './DataService'; + +import '@progress/kendo-theme-default/dist/all.css'; +// import '@progress/kendo-theme-material/dist/all.css'; + +const App = () => { + return ( +
+ + + +
+ ); +} + +export default App; diff --git a/examples/kendo-react-grid-performance/src/App.test.js b/examples/kendo-react-grid-performance/src/App.test.js new file mode 100644 index 00000000..1f03afee --- /dev/null +++ b/examples/kendo-react-grid-performance/src/App.test.js @@ -0,0 +1,8 @@ +import { render, screen } from '@testing-library/react'; +import App from './App'; + +test('renders learn react link', () => { + render(); + const linkElement = screen.getByText(/learn react/i); + expect(linkElement).toBeInTheDocument(); +}); diff --git a/examples/kendo-react-grid-performance/src/DataService/index.jsx b/examples/kendo-react-grid-performance/src/DataService/index.jsx new file mode 100644 index 00000000..9d43d635 --- /dev/null +++ b/examples/kendo-react-grid-performance/src/DataService/index.jsx @@ -0,0 +1,123 @@ +import React from 'react'; +import { FinancialData } from './local-data/financial-data'; + +export const updateRandomPrices = (data) => { + // console.time('randomizerandom'); + + const newData = data.slice(); + for (let i = Math.round(Math.random() * 10); i < newData.length; i += Math.round(Math.random() * 10)) { + randomizeObjectData(newData[i]); + } + + // console.timeEnd('randomizerandom'); + + return newData; +} + +export const updateAllPrices = (data) => { + // console.time('randomizeall'); + + const newData = data.slice(); + for (const dataRow of newData) { + randomizeObjectData(dataRow); + } + + // console.timeEnd('randomizeall'); + return newData; +} + +export const randomizeObjectData = (dataObj) => { + const changeP = 'Change(%)'; + const res = generateNewPrice(dataObj.Price); + dataObj.Change = res.Price - dataObj.Price; + dataObj.Price = res.Price; + dataObj[changeP] = res.ChangePercent; +} + +export const generateNewPrice = (oldPrice) => { + let rnd = Math.random(); + rnd = Math.round(rnd * 100) / 100; + const volatility = 2; + let newPrice = 0; + let changePercent = 2 * volatility * rnd; + if (changePercent > volatility) { + changePercent -= (2 * volatility); + } + const changeAmount = oldPrice * (changePercent / 100); + newPrice = oldPrice + changeAmount; + newPrice = Math.round(newPrice * 100) / 100; + const result = { Price: 0, ChangePercent: 0 }; + changePercent = Math.round(changePercent * 100) / 100; + result.Price = newPrice; + result.ChangePercent = changePercent; + + return result; +} + + +export const DataProvider = (props) => { + const [data, setData] = React.useState([]); + const intervalRef = React.useRef(null); + + const onDataReset = React.useCallback( + (volume) => { + // console.log(volume, 'inside onDataReset') + clearInterval(intervalRef.current); + + const financialData = new FinancialData(); + setData(financialData.generateData(volume)); + }, + [] + ); + + const onStartLiveUpdate = React.useCallback( + (interval) => { + clearInterval(intervalRef.current); + + intervalRef.current = setInterval( + () => setData(oldData => updateRandomPrices(oldData)), + interval + ); + }, + [] + ); + + const onStartLiveUpdateAll = React.useCallback( + (interval) => { + clearInterval(intervalRef.current); + + intervalRef.current = setInterval( + () => { + setData(oldData => updateAllPrices(oldData)) + }, + interval + ); + }, + [] + ); + + const onStopLiveUpdate = React.useCallback( + () => { + clearInterval(intervalRef.current); + }, + [] + ); + + return React.Children.map( + props.children, + (child) => { + if (!React.isValidElement(child)) { + return; + } + + return ; + } + ); +} diff --git a/examples/kendo-react-grid-performance/src/DataService/local-data/financial-data.js b/examples/kendo-react-grid-performance/src/DataService/local-data/financial-data.js new file mode 100644 index 00000000..0dd8008a --- /dev/null +++ b/examples/kendo-react-grid-performance/src/DataService/local-data/financial-data.js @@ -0,0 +1,422 @@ +/* tslint:disable */ +export const COUNTRIES = [ + { + "Country": "America", + "Cities": ["New York", "Chicago", "Dallas", "Los Angeles", "Boston", "Austin", "Florida"] + }, + { + "Country": "Germany", + "Cities": ["Dortmund", "Stuttgard", "München", "Berlin", "Hamburg"] + }, + { + "Country": "United Kingdom", + "Cities": ["Manchester", "Liverpool", "Sheffield", "Leeds", "Birmingham", "London"] + }, + { + "Country": "Italy", + "Cities": ["Milano", "Firenze", "Roma", "Napoli", "Catania", "Palermo", "Venezia"] + }, + { + "Country": "France", + "Cities": ["Nantes", "Paris", "Toulouse", "Montpellier", "Marseille", "Nice", "Lyon"] + }, + { + "Country": "Spain", + "Cities": ["Madrid", "Zaragoza", "València", "Murcia", "Málaga", "Barcelona"] + }, + +] + +export const DealType = [ + "Buy", "Sell" +] + +export const Stock = [ + "OX", "USD", "SHIB", "TETHERUS", "XEC", "18C", "BITCOIN", "ETHERIUM", "INCH", "BUSD", "GBP", "EUR" +] + +export const Settlement = [ + "Deliverable", "Cash" +] + +export const MOCKFINANCEDATA = [ + { + "Category": "Test Data", + "Industry": "Informational technology", + "Sector": "Technology", + "SubSector": "Machine learning", + "SectorType": "PRIVATE", + // tslint:disable-next-line:object-literal-sort-keys + "Type": "DEFAULT", + "FullName": "Progress Software", + "Location": "Boston", + "FitchRating": "Private", + "DBRSRating": "Private", + "Currency": "USD", + "SecurityCode": "12345678910 IT", + "SectorCode": "IT", + "CUSIP": "987654321", + "Ticker": "PGRS", + "Cpn": "40.875", + "Maturity": "1981", + "KRD_5YR": 2.00006, + "Address": "Bedford", + "Phone": "+1 111 111 111", + "Number": 28.302, + "KRD_10YR": 0, + "KRD_1YR": -0.00187, + "SpecialCode": null + }]; + +export const DATA = [ + { + "Ticker": "OX", + "Spread": 0.01, + "Open Price": 1281.10, + "Price": 1280.7317, + "Buy": 1280.7267, + "Sell": 1280.7367, + "Change": -0.3683, + "Change(%)": -0.0287, + "Volume": 48387, + "High(D)": 1289.50, + "Low(D)": 1279.10, + "High(Y)": 1306, + "Low(Y)": 1047.20, + "Start(Y)": 1176.60, + "Change On Year(%)": 8.8502 + }, + { + "Ticker": "USD", + "Type": "Silver", + "Spread": 0.01, + "Open Price": 17.43, + "Price": 17.42, + "Buy": 17.43, + "Sell": 17.43, + "Change": -0.01, + "Change(%)": -0.0574, + "Volume": 11720, + "High(D)": 17.51, + "Low(D)": 17.37, + "High(Y)": 18.06, + "Low(Y)": 13.73, + "Start(Y)": 15.895, + "Change On Year(%)": 9.5942 + }, + { + "Ticker": "SHIB", + "Type": "Copper", + "Spread": 0.02, + "Open Price": 2.123, + "Price": 2.113, + "Buy": 2.123, + "Sell": 2.123, + "Change": -0.01, + "Change(%)": -0.471, + "Volume": 28819, + "High(D)": 2.16, + "Low(D)": 2.11, + "High(Y)": 2.94, + "Low(Y)": 1.96, + "Start(Y)": 2.45, + "Change On Year(%)": -13.7551 + }, + { + "Ticker": "TETHERUS", + "Type": "Platinum", + "Spread": 0.01, + "Open Price": 1071.60, + "Price": 1071.0993, + "Buy": 1071.0943, + "Sell": 1071.1043, + "Change": -0.5007, + "Change(%)": -0.0467, + "Volume": 3039, + "High(D)": 1081.20, + "Low(D)": 1070.50, + "High(Y)": 1120.60, + "Low(Y)": 812.40, + "Start(Y)": 966.50, + "Change On Year(%)": 10.8225 + }, + { + "Ticker": "XEC", + "Type": "Palladium", + "Spread": 0.01, + "Open Price": 600.55, + "Price": 601.0005, + "Buy": 600.9955, + "Sell": 601.0055, + "Change": 0.4505, + "Change(%)": 0.075, + "Volume": 651, + "High(D)": 607.20, + "Low(D)": 598.40, + "High(Y)": 690, + "Low(Y)": 458.6, + "Start(Y)": 574.3, + "Change On Year(%)": 4.6492 + }, + { + "Ticker": "18C", + "Type": "Oil", + "Spread": 0.015, + "Open Price": 45.54, + "Price": 45.7899, + "Buy": 45.7824, + "Sell": 45.7974, + "Change": 0.2499, + "Change(%)": 0.5487, + "Volume": 107196, + "High(D)": 45.94, + "Low(D)": 45.00, + "High(Y)": 65.28, + "Low(Y)": 30.79, + "Start(Y)": 48.035, + "Change On Year(%)": -4.6739 + }, + { + "Ticker": "BITCOIN", + "Type": "Brent", + "Spread": 0.01, + "Open Price": 46.06, + "Price": 46.05, + "Buy": 46.06, + "Sell": 46.06, + "Change": -0.01, + "Change(%)": -0.0217, + "Volume": 59818, + "High(D)": 46.48, + "Low(D)": 45.60, + "High(Y)": 71.14, + "Low(Y)": 30.02, + "Start(Y)": 50.58, + "Change On Year(%)": -8.9561 + }, + { + "Ticker": "ETHERIUM", + "Type": "Natural Gas", + "Spread": 0.02, + "Open Price": 2.094, + "Price": 2.104, + "Buy": 2.094, + "Sell": 2.094, + "Change": 0.01, + "Change(%)": 0.4776, + "Volume": 2783, + "High(D)": 2.11, + "Low(D)": 2.09, + "High(Y)": 3.20, + "Low(Y)": 1.84, + "Start(Y)": 2.52, + "Change On Year(%)": -16.5079 + }, + { + "Ticker": "INCH", + "Type": "RBOB Gas", + "Spread": 0.015, + "Open Price": 1.5086, + "Price": 1.9532, + "Buy": 1.9457, + "Sell": 1.9607, + "Change": 0.4446, + "Change(%)": 29.4686, + "Volume": 2646, + "High(D)": 1.9532, + "Low(D)": 1.50, + "High(Y)": 2.05, + "Low(Y)": 1.15, + "Start(Y)": 1.60, + "Change On Year(%)": 22.0727 + }, + { + "Ticker": "BUSD", + "Type": "Diesel", + "Spread": 0.015, + "Open Price": 1.3474, + "Price": 1.3574, + "Buy": 1.3474, + "Sell": 1.3474, + "Change": 0.01, + "Change(%)": 0.7422, + "Volume": 2971, + "High(D)": 1.36, + "Low(D)": 1.34, + "High(Y)": 2.11, + "Low(Y)": 0.92, + "Start(Y)": 1.515, + "Change On Year(%)": -10.4026 + }, + { + "Ticker": "GBP", + "Type": "Ethanol", + "Spread": 0.01, + "Open Price": 1.512, + "Price": 2.7538, + "Buy": 2.7488, + "Sell": 2.7588, + "Change": 1.2418, + "Change(%)": 82.1323, + "Volume": 14, + "High(D)": 2.7538, + "Low(D)": 1.1168, + "High(Y)": 2.7538, + "Low(Y)": 1.1168, + "Start(Y)": 1.475, + "Change On Year(%)": 86.7011 + }, + { + "Ticker": "EUR", + "Type": "Uranium", + "Spread": 0.02, + "Open Price": 27.55, + "Price": 27.58, + "Buy": 27.55, + "Sell": 27.55, + "Change": 0.03, + "Change(%)": 0.1089, + "Volume": 12, + "High(D)": 27.55, + "Low(D)": 27.55, + "High(Y)": 29.32, + "Low(Y)": 21.28, + "Start(Y)": 25.30, + "Change On Year(%)": 9.0119 + } +]; + +/* tslint:enable */ +export class FinancialData { + generateData(count) { + console.time('generateData'); + const currData = []; + for (let i = 0; i < count; i++) { + const rand = Math.floor(Math.random() * Math.floor(DATA.length)); + const dataObj = Object.assign({}, DATA[rand]); + + dataObj.Settlement = Settlement[this.generateRandomNumber(0, 1)]; + dataObj.Stock = Stock[this.generateRandomNumber(0, 11)]; + const country = COUNTRIES[this.generateRandomNumber(0, 5)]; + dataObj.Country = country.Country; + dataObj.City = this.randomizeCity(country); + // for (let y = 0; y < 80; y++) { + // dataObj["Text" + y] = "Text"; + // } + + for (const mockData of MOCKFINANCEDATA) { + for (const prop in mockData) { + if (mockData.hasOwnProperty(prop)) { + dataObj[prop] = mockData[prop]; + } + } + } + + dataObj.ID = i; + this.randomizeObjectData(dataObj); + currData.push(dataObj); + } + console.timeEnd('generateData'); + return currData; + } + + updateAllPrices(data) { + const currData = []; + for (const dataRow of data) { + const dataObj = Object.assign({}, dataRow); + this.randomizeObjectData(dataObj); + currData.push(dataObj); + } + return currData; + } + + updateRandomPrices(data) { + const currData = data.slice(0, data.length + 1); + // let y = 0; + for (let i = Math.round(Math.random() * 10); i < data.length; i += Math.round(Math.random() * 10)) { + const dataObj = Object.assign({}, data[i]); + this.randomizeObjectData(dataObj); + currData[i] = dataObj; + // y++; + } + // return {data: currData, recordsUpdated: y }; + return currData; + } + + updateRandomPrices2(data) { + const currData = data.slice(0, data.length + 1); + let y = 0; + for (let i = Math.round(Math.random() * 10); i < data.length; i += Math.round(Math.random() * 10)) { + const dataObj = Object.assign({}, data[i]); + this.randomizeObjectData(dataObj); + currData[i] = dataObj; + y++; + } + return { data: currData, recordsUpdated: y }; + } + + randomizeObjectData(dataObj) { + const changeP = 'Change(%)'; + const res = this.generateNewPrice(dataObj.Price); + dataObj.Change = res.Price - dataObj.Price; + dataObj.Price = res.Price; + dataObj[changeP] = res.ChangePercent; + } + + generateNewPrice(oldPrice) { + const rnd = parseFloat(Math.random().toFixed(2)); + const volatility = 2; + let newPrice = 0; + + let changePercent = 2 * volatility * rnd; + if (changePercent > volatility) { + changePercent -= (2 * volatility); + } + + const changeAmount = oldPrice * (changePercent / 100); + newPrice = oldPrice + changeAmount; + + const result = { Price: 0, ChangePercent: 0 }; + result.Price = parseFloat(newPrice.toFixed(2)); + result.ChangePercent = parseFloat(changePercent.toFixed(2)); + + return result; + } + + generateRandomNumber(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + } + + randomizeCity(country) { + let city; + switch (country.Country) { + case 'America': { + city = country.Cities[this.generateRandomNumber(0, 6)]; + break; + } + case 'Germany': { + city = country.Cities[this.generateRandomNumber(0, 4)]; + break; + } + case 'United Kingdom': { + city = country.Cities[this.generateRandomNumber(0, 5)]; + break; + } + case 'Italy': { + city = country.Cities[this.generateRandomNumber(0, 6)]; + break; + } + case 'France': { + city = country.Cities[this.generateRandomNumber(0, 6)]; + break; + } + case 'Spain': { + city = country.Cities[this.generateRandomNumber(0, 5)]; + break; + } + default: + break; + } + return city; + } +} \ No newline at end of file diff --git a/examples/kendo-react-grid-performance/src/DataService/local-data/trends.js b/examples/kendo-react-grid-performance/src/DataService/local-data/trends.js new file mode 100644 index 00000000..00808f1b --- /dev/null +++ b/examples/kendo-react-grid-performance/src/DataService/local-data/trends.js @@ -0,0 +1,23 @@ +export const negative = (rowData) => { + return rowData['Change(%)'] < 0; +}; + +export const positive = (rowData) => { + return rowData['Change(%)'] > 0; +}; + +export const changeNegative = (rowData) => { + return rowData['Change(%)'] < 0 && rowData['Change(%)'] > -1; +}; + +export const changePositive = (rowData) => { + return rowData['Change(%)'] > 0 && rowData['Change(%)'] < 1; +}; + +export const strongPositive = (rowData) => { + return rowData['Change(%)'] >= 1; +}; + +export const strongNegative = (rowData) => { + return rowData['Change(%)'] <= -1; +}; diff --git a/examples/kendo-react-grid-performance/src/KendoGrid/column-menu.jsx b/examples/kendo-react-grid-performance/src/KendoGrid/column-menu.jsx new file mode 100644 index 00000000..385dfb08 --- /dev/null +++ b/examples/kendo-react-grid-performance/src/KendoGrid/column-menu.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { + GridColumnMenuSort, + GridColumnMenuFilter +} from '@progress/kendo-react-grid'; + +export const ColumnMenu = (props) => { + return ( +
+ + +
+ ); +} \ No newline at end of file diff --git a/examples/kendo-react-grid-performance/src/KendoGrid/index.jsx b/examples/kendo-react-grid-performance/src/KendoGrid/index.jsx new file mode 100644 index 00000000..dcb369af --- /dev/null +++ b/examples/kendo-react-grid-performance/src/KendoGrid/index.jsx @@ -0,0 +1,415 @@ +import React from 'react'; +import { Grid, GridColumn } from '@progress/kendo-react-grid'; +import { Switch, Slider } from '@progress/kendo-react-inputs'; +import { Button, ButtonGroup } from '@progress/kendo-react-buttons'; +import { + Card, + CardHeader, + CardTitle, + CardBody + } from "@progress/kendo-react-layout"; + import { useInternationalization } from "@progress/kendo-react-intl"; + import { process } from '@progress/kendo-data-query'; + +import { debounce } from '../utils'; +import { ColumnMenu } from './column-menu'; +import { ChangeCell, ChangePercentCell, PriceCell, ChartCell } from './templates'; + +const DEBOUNCE_TIME = 250; +const VOLUME_INITIAL = 10000; +const VOLUME_STEP = 1000; +const FREQUENCY_INITIAL = 100; +const FREQUENCY_STEP = 100; +const PAGE_SIZE = 25; + + +const BUTTON_META = { + live: { + title: 'Live Prices', + icon: 'refresh' + }, + liveAll: { + title: 'Live All Prices', + icon: 'refresh' + }, + stop: { + title: 'Stop', + icon: 'stop' + } +}; + +export const KendoGrid = (props) => { + const intl = useInternationalization(); + // const [_, setForceUpdate] = React.useState(false); + + + const [realUpdate, setRealUpdate] = React.useState(false); + const [updateTime, setUpdateTime] = React.useState([]); + const [averageUpdate, setAverageUpdate] = React.useState(0); + + const [showColumnMenu, setShowColumnMenu] = React.useState(true); + const [enableColumnVirtualization, setEnableColumnVirtualization] = React.useState(true); + const [grouping, setGrouping] = React.useState(false); + + const [dataState, setDataState] = React.useState({ + // NOTE: can be split on different states for better performance and change detection + group: [], + sort: [], + skip: 0, + take: PAGE_SIZE + }) + const volumeRef = React.useRef(VOLUME_INITIAL); + const frequencyRef = React.useRef(FREQUENCY_INITIAL); + const [selectedButton, setSelectedButton] = React.useState("stop"); + const [liveUpdating, setLiveUpdating] = React.useState(false); + // const lastSelectedIndexRef = React.useRef(0); + const debouncedDataReset = React.useMemo( + () => debounce((currentVolume) => { + props.onDataReset(currentVolume) + }, DEBOUNCE_TIME), + [props] + ); + + React.useEffect( + () => { + props.onDataReset(volumeRef.current); + }, + [props] + ); + + React.useEffect(() => { + setRealUpdate(true); + }, [props.data, dataState]) + + const onDataStateChange = React.useCallback( + (event) => { + setDataState(event.dataState); + }, + [] + ); + + const onColumnMenuChange = () => { + setShowColumnMenu(!showColumnMenu); + } + + const onColumnVirtualizationChange = () => { + setEnableColumnVirtualization(!enableColumnVirtualization); + } + + const onGroupingChange = () => { + setGrouping(!grouping); + } + + const onVolumeChange = React.useCallback( + (event) => { + const currentVolume = Math.floor(event.value / VOLUME_STEP) * VOLUME_STEP; + volumeRef.current = currentVolume; + debouncedDataReset(currentVolume); + }, + [debouncedDataReset] + ); + + const onFrequencyChange = React.useCallback( + (event) => { + frequencyRef.current = Math.floor(event.value / FREQUENCY_STEP) * FREQUENCY_STEP; + debouncedDataReset(volumeRef.current); + }, + [debouncedDataReset] + ); + + const onSelectClick = React.useCallback( + (event) => { + const currentButton = event.currentTarget.name; + setSelectedButton(currentButton); + switch (currentButton) { + case "live": + props.onStartLiveUpdate(frequencyRef.current); + setLiveUpdating(true); + break; + case "liveAll": + props.onStartLiveUpdateAll(frequencyRef.current); + setLiveUpdating(true); + break; + case "stop": + props.onStopLiveUpdate(); + setLiveUpdating(false); + break; + default: + } + }, + [props] + ); + + const onProfilerCallback = ( + id, // the "id" prop of the Profiler tree that has just committed + phase, // either "mount" (if the tree just mounted) or "update" (if it re-rendered) + actualDuration, // time spent rendering the committed update + baseDuration, // estimated time to render the entire subtree without memoization + startTime, // when React began rendering this update + commitTime, // when React committed this update + interactions // the Set of interactions belonging to this update + ) => { + if (realUpdate) { + let currentUpdateTime = [...updateTime]; + currentUpdateTime.push(actualDuration); + // console.log(updateTime, 'update time') + setRealUpdate(false); + setUpdateTime(currentUpdateTime); + const sum = updateTime.reduce((a, b) => a + b, 0); + const avg = (sum / updateTime.length) || 0; + setAverageUpdate(avg); + } + } + + //console.time('processdata'); + const processedData = process(props.data, dataState); + //console.timeEnd('processdata'); + + return ( + <> +
+
+
+
+ +  Column Menu +
+
+
+
+ +  Column Virtualization +
+
+
+
+ +  Grouping +
+
+
+
+
+ +
+
+ +
+
+
+
+ + { + Object + .keys(BUTTON_META) + .filter(key => BUTTON_META[key] !== 'number') + .map( + (key) => ( + + ) + ) + } + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + Performance results + + +

Last update took: {intl.formatNumber(updateTime[updateTime.length - 1], 'n5')} milliseconds

+

Average time for an update is: {intl.formatNumber(averageUpdate, 'n5')} milliseconds

+
+
+
+
+
+ + ); +} + +KendoGrid.displayName = 'KendoGrid'; diff --git a/examples/kendo-react-grid-performance/src/KendoGrid/templates.jsx b/examples/kendo-react-grid-performance/src/KendoGrid/templates.jsx new file mode 100644 index 00000000..df072ae2 --- /dev/null +++ b/examples/kendo-react-grid-performance/src/KendoGrid/templates.jsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { Button } from '@progress/kendo-react-buttons'; +import { classNames } from '@progress/kendo-react-common'; +import { formatNumber } from '@progress/kendo-intl'; +import { trends } from "../utils"; + +export const PriceCell = (props) => { + const { dataItem, field, className } = props; + const currentTrends = trends(dataItem); + + return ( + +
+ {formatNumber(dataItem[field], 'c4', 'en')} + { + currentTrends.positive && () + } + { + currentTrends.negative && () + } +
+ + ); +}; + +export const ChangeCell = (props) => { + const { dataItem, field, className } = props; + const currentTrends = trends(dataItem); + + return ( + +
{formatNumber(dataItem[field], 'n', 'en')}
+ + ); +}; + +export const ChangePercentCell = (props) => { + const { dataItem, field, className } = props; + const currentTrends = trends(dataItem); + + return ( + +
{formatNumber(dataItem[field], '0.##', 'en')}%
+ + ); +}; + +export const ChartCell = (props) => { + const { className } = props; + + return ( + +
+ + { + this.renderColumns(columns) + } + + ); + } +} + +GridHOC = connect(mapStateToProps, mapDispatchToProps)(GridHOC); + +export default GridHOC \ No newline at end of file diff --git a/examples/kendo-react-redux-form/src/components/ReduxForm.js b/examples/kendo-react-redux-form/src/components/ReduxForm.js new file mode 100644 index 00000000..08374c78 --- /dev/null +++ b/examples/kendo-react-redux-form/src/components/ReduxForm.js @@ -0,0 +1,111 @@ +import React from 'react' +import { Field, reduxForm, FieldArray } from 'redux-form'; +import { connect } from 'react-redux'; +import { Button } from '@progress/kendo-react-buttons'; +import FormGrid from './FormGrid'; +import products from './../data/products.json'; +import { + KendoInput, + KendoNumericTextBox, + KendoDatePicker, +} from './Editors'; +import { required, minValue } from './validations'; + +const minValueZero = minValue(0); + +let ReduxProductsForm = props => { + const { handleSubmit, reset } = props + + return (
+
+
Order
+
+
+
+ + + + + +
Order Products
+ +
+ + +
+
+
+
); +} + +ReduxProductsForm = reduxForm({ + form: 'order' +})(ReduxProductsForm) + +ReduxProductsForm = connect((state, initialProps) => ({ + initialValues: initialProps.initialFormValues ? initialProps.initialFormValues : {} +}) +)(ReduxProductsForm); + +export default ReduxProductsForm \ No newline at end of file diff --git a/examples/kendo-react-redux-form/src/components/RemoveCell.jsx b/examples/kendo-react-redux-form/src/components/RemoveCell.jsx new file mode 100644 index 00000000..8c23e013 --- /dev/null +++ b/examples/kendo-react-redux-form/src/components/RemoveCell.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { Button } from '@progress/kendo-react-buttons'; + +export default class RemoveCell extends React.Component { + render() { + const { onRemove } = this.props; + + return + } +} \ No newline at end of file diff --git a/examples/kendo-react-redux-form/src/components/validations.js b/examples/kendo-react-redux-form/src/components/validations.js new file mode 100644 index 00000000..5d412933 --- /dev/null +++ b/examples/kendo-react-redux-form/src/components/validations.js @@ -0,0 +1,3 @@ +export const required = value => value ? undefined : 'Required*'; +export const minValue = min => value => + value && value < min ? `Must be at least ${min}` : undefined; diff --git a/examples/kendo-react-redux-form/src/data/categories.json b/examples/kendo-react-redux-form/src/data/categories.json new file mode 100644 index 00000000..8f15f5b6 --- /dev/null +++ b/examples/kendo-react-redux-form/src/data/categories.json @@ -0,0 +1,43 @@ + +[ + { + "CategoryID" : 2, + "Description" : "Sweet and savory sauces relishes spreads and seasonings", + "CategoryName" : "Condiments" + }, + { + "CategoryID" : 1, + "Description" : "Soft drinks coffees teas beers and ales", + "CategoryName" : "Beverages" + }, + { + "CategoryID" : 3, + "Description" : "Desserts candies and sweet breads", + "CategoryName" : "Confections" + }, + { + "CategoryID" : 4, + "Description" : "Cheeses", + "CategoryName" : "Dairy Products" + }, + { + "CategoryID" : 5, + "Description" : "Breads crackers pasta and cereal", + "CategoryName" : "Grains/Cereals" + }, + { + "CategoryID" : 6, + "Description" : "Prepared meats", + "CategoryName" : "Meat/Poultry" + }, + { + "CategoryID" : 7, + "Description" : "Dried fruit and bean curd", + "CategoryName" : "Produce" + }, + { + "CategoryID" : 8, + "Description" : "Seaweed and fish", + "CategoryName" : "Seafood" + } +] \ No newline at end of file diff --git a/examples/kendo-react-redux-form/src/data/order.json b/examples/kendo-react-redux-form/src/data/order.json new file mode 100644 index 00000000..e1ff9447 --- /dev/null +++ b/examples/kendo-react-redux-form/src/data/order.json @@ -0,0 +1,36 @@ +{ + "OrderID" : 10266, + "CustomerID" : "WARTH", + "EmployeeID" : 3, + "OrderDate" : "1996-07-26 00:00:00.000", + "RequiredDate" : "1996-09-06 00:00:00.000", + "ShippedDate" : "1996-07-31 00:00:00.000", + "ShipVia" : 3, + "Freight" : 25.73, + "ShipName" : "Wartian Herkku", + "ShipAddress" : { + "Street" : "Torikatu 38", + "City" : "Oulu", + "Region" : "NULL", + "PostalCode" : 90110, + "Country" : "Finland" + }, + "Details" : [ + { + "Product" : { + "ProductID": 33, + "ProductName": "Geitost" + }, + "Quantity" : 24, + "Discount" : 0 + }, + { + "Product" : { + "ProductID": 35, + "ProductName": "Steeleye Stout" + }, + "Quantity" : 12, + "Discount" : 0.05 + } + ] +} \ No newline at end of file diff --git a/examples/kendo-react-redux-form/src/data/products.json b/examples/kendo-react-redux-form/src/data/products.json new file mode 100644 index 00000000..8fbee4e8 --- /dev/null +++ b/examples/kendo-react-redux-form/src/data/products.json @@ -0,0 +1,1233 @@ +[{ + "ProductID" : 1, + "ProductName" : "Chai", + "SupplierID" : 1, + "CategoryID" : 1, + "QuantityPerUnit" : "10 boxes x 20 bags", + "UnitPrice" : 18.0000, + "UnitsInStock" : 39, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 2, + "ProductName" : "Chang", + "SupplierID" : 1, + "CategoryID" : 1, + "QuantityPerUnit" : "24 - 12 oz bottles", + "UnitPrice" : 19.0000, + "UnitsInStock" : 17, + "UnitsOnOrder" : 40, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 3, + "ProductName" : "Aniseed Syrup", + "SupplierID" : 1, + "CategoryID" : 2, + "QuantityPerUnit" : "12 - 550 ml bottles", + "UnitPrice" : 10.0000, + "UnitsInStock" : 13, + "UnitsOnOrder" : 70, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 4, + "ProductName" : "Chef Anton's Cajun Seasoning", + "SupplierID" : 2, + "CategoryID" : 2, + "QuantityPerUnit" : "48 - 6 oz jars", + "UnitPrice" : 22.0000, + "UnitsInStock" : 53, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 5, + "ProductName" : "Chef Anton's Gumbo Mix", + "SupplierID" : 2, + "CategoryID" : 2, + "QuantityPerUnit" : "36 boxes", + "UnitPrice" : 21.3500, + "UnitsInStock" : 0, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 6, + "ProductName" : "Grandma's Boysenberry Spread", + "SupplierID" : 3, + "CategoryID" : 2, + "QuantityPerUnit" : "12 - 8 oz jars", + "UnitPrice" : 25.0000, + "UnitsInStock" : 120, + "UnitsOnOrder" : 0, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 7, + "ProductName" : "Uncle Bob's Organic Dried Pears", + "SupplierID" : 3, + "CategoryID" : 7, + "QuantityPerUnit" : "12 - 1 lb pkgs.", + "UnitPrice" : 30.0000, + "UnitsInStock" : 15, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 7, + "CategoryName" : "Produce", + "Description" : "Dried fruit and bean curd" + } +}, { + "ProductID" : 8, + "ProductName" : "Northwoods Cranberry Sauce", + "SupplierID" : 3, + "CategoryID" : 2, + "QuantityPerUnit" : "12 - 12 oz jars", + "UnitPrice" : 40.0000, + "UnitsInStock" : 6, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 9, + "ProductName" : "Mishi Kobe Niku", + "SupplierID" : 4, + "CategoryID" : 6, + "QuantityPerUnit" : "18 - 500 g pkgs.", + "UnitPrice" : 97.0000, + "UnitsInStock" : 29, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 6, + "CategoryName" : "Meat/Poultry", + "Description" : "Prepared meats" + } +}, { + "ProductID" : 10, + "ProductName" : "Ikura", + "SupplierID" : 4, + "CategoryID" : 8, + "QuantityPerUnit" : "12 - 200 ml jars", + "UnitPrice" : 31.0000, + "UnitsInStock" : 31, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 11, + "ProductName" : "Queso Cabrales", + "SupplierID" : 5, + "CategoryID" : 4, + "QuantityPerUnit" : "1 kg pkg.", + "UnitPrice" : 21.0000, + "UnitsInStock" : 22, + "UnitsOnOrder" : 30, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 12, + "ProductName" : "Queso Manchego La Pastora", + "SupplierID" : 5, + "CategoryID" : 4, + "QuantityPerUnit" : "10 - 500 g pkgs.", + "UnitPrice" : 38.0000, + "UnitsInStock" : 86, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 13, + "ProductName" : "Konbu", + "SupplierID" : 6, + "CategoryID" : 8, + "QuantityPerUnit" : "2 kg box", + "UnitPrice" : 6.0000, + "UnitsInStock" : 24, + "UnitsOnOrder" : 0, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 14, + "ProductName" : "Tofu", + "SupplierID" : 6, + "CategoryID" : 7, + "QuantityPerUnit" : "40 - 100 g pkgs.", + "UnitPrice" : 23.2500, + "UnitsInStock" : 35, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 7, + "CategoryName" : "Produce", + "Description" : "Dried fruit and bean curd" + } +}, { + "ProductID" : 15, + "ProductName" : "Genen Shouyu", + "SupplierID" : 6, + "CategoryID" : 2, + "QuantityPerUnit" : "24 - 250 ml bottles", + "UnitPrice" : 15.5000, + "UnitsInStock" : 39, + "UnitsOnOrder" : 0, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 16, + "ProductName" : "Pavlova", + "SupplierID" : 7, + "CategoryID" : 3, + "QuantityPerUnit" : "32 - 500 g boxes", + "UnitPrice" : 17.4500, + "UnitsInStock" : 29, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 17, + "ProductName" : "Alice Mutton", + "SupplierID" : 7, + "CategoryID" : 6, + "QuantityPerUnit" : "20 - 1 kg tins", + "UnitPrice" : 39.0000, + "UnitsInStock" : 0, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 6, + "CategoryName" : "Meat/Poultry", + "Description" : "Prepared meats" + } +}, { + "ProductID" : 18, + "ProductName" : "Carnarvon Tigers", + "SupplierID" : 7, + "CategoryID" : 8, + "QuantityPerUnit" : "16 kg pkg.", + "UnitPrice" : 62.5000, + "UnitsInStock" : 42, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 19, + "ProductName" : "Teatime Chocolate Biscuits", + "SupplierID" : 8, + "CategoryID" : 3, + "QuantityPerUnit" : "10 boxes x 12 pieces", + "UnitPrice" : 9.2000, + "UnitsInStock" : 25, + "UnitsOnOrder" : 0, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 20, + "ProductName" : "Sir Rodney's Marmalade", + "SupplierID" : 8, + "CategoryID" : 3, + "QuantityPerUnit" : "30 gift boxes", + "UnitPrice" : 81.0000, + "UnitsInStock" : 40, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 21, + "ProductName" : "Sir Rodney's Scones", + "SupplierID" : 8, + "CategoryID" : 3, + "QuantityPerUnit" : "24 pkgs. x 4 pieces", + "UnitPrice" : 10.0000, + "UnitsInStock" : 3, + "UnitsOnOrder" : 40, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 22, + "ProductName" : "Gustaf's Knäckebröd", + "SupplierID" : 9, + "CategoryID" : 5, + "QuantityPerUnit" : "24 - 500 g pkgs.", + "UnitPrice" : 21.0000, + "UnitsInStock" : 104, + "UnitsOnOrder" : 0, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 23, + "ProductName" : "Tunnbröd", + "SupplierID" : 9, + "CategoryID" : 5, + "QuantityPerUnit" : "12 - 250 g pkgs.", + "UnitPrice" : 9.0000, + "UnitsInStock" : 61, + "UnitsOnOrder" : 0, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 24, + "ProductName" : "Guaraná Fantástica", + "SupplierID" : 10, + "CategoryID" : 1, + "QuantityPerUnit" : "12 - 355 ml cans", + "UnitPrice" : 4.5000, + "UnitsInStock" : 20, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 25, + "ProductName" : "NuNuCa Nuß-Nougat-Creme", + "SupplierID" : 11, + "CategoryID" : 3, + "QuantityPerUnit" : "20 - 450 g glasses", + "UnitPrice" : 14.0000, + "UnitsInStock" : 76, + "UnitsOnOrder" : 0, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 26, + "ProductName" : "Gumbär Gummibärchen", + "SupplierID" : 11, + "CategoryID" : 3, + "QuantityPerUnit" : "100 - 250 g bags", + "UnitPrice" : 31.2300, + "UnitsInStock" : 15, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 27, + "ProductName" : "Schoggi Schokolade", + "SupplierID" : 11, + "CategoryID" : 3, + "QuantityPerUnit" : "100 - 100 g pieces", + "UnitPrice" : 43.9000, + "UnitsInStock" : 49, + "UnitsOnOrder" : 0, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 28, + "ProductName" : "Rössle Sauerkraut", + "SupplierID" : 12, + "CategoryID" : 7, + "QuantityPerUnit" : "25 - 825 g cans", + "UnitPrice" : 45.6000, + "UnitsInStock" : 26, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 7, + "CategoryName" : "Produce", + "Description" : "Dried fruit and bean curd" + } +}, { + "ProductID" : 29, + "ProductName" : "Thüringer Rostbratwurst", + "SupplierID" : 12, + "CategoryID" : 6, + "QuantityPerUnit" : "50 bags x 30 sausgs.", + "UnitPrice" : 123.7900, + "UnitsInStock" : 0, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 6, + "CategoryName" : "Meat/Poultry", + "Description" : "Prepared meats" + } +}, { + "ProductID" : 30, + "ProductName" : "Nord-Ost Matjeshering", + "SupplierID" : 13, + "CategoryID" : 8, + "QuantityPerUnit" : "10 - 200 g glasses", + "UnitPrice" : 25.8900, + "UnitsInStock" : 10, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 31, + "ProductName" : "Gorgonzola Telino", + "SupplierID" : 14, + "CategoryID" : 4, + "QuantityPerUnit" : "12 - 100 g pkgs", + "UnitPrice" : 12.5000, + "UnitsInStock" : 0, + "UnitsOnOrder" : 70, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 32, + "ProductName" : "Mascarpone Fabioli", + "SupplierID" : 14, + "CategoryID" : 4, + "QuantityPerUnit" : "24 - 200 g pkgs.", + "UnitPrice" : 32.0000, + "UnitsInStock" : 9, + "UnitsOnOrder" : 40, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 33, + "ProductName" : "Geitost", + "SupplierID" : 15, + "CategoryID" : 4, + "QuantityPerUnit" : "500 g", + "UnitPrice" : 2.5000, + "UnitsInStock" : 112, + "UnitsOnOrder" : 0, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 34, + "ProductName" : "Sasquatch Ale", + "SupplierID" : 16, + "CategoryID" : 1, + "QuantityPerUnit" : "24 - 12 oz bottles", + "UnitPrice" : 14.0000, + "UnitsInStock" : 111, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 35, + "ProductName" : "Steeleye Stout", + "SupplierID" : 16, + "CategoryID" : 1, + "QuantityPerUnit" : "24 - 12 oz bottles", + "UnitPrice" : 18.0000, + "UnitsInStock" : 20, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 36, + "ProductName" : "Inlagd Sill", + "SupplierID" : 17, + "CategoryID" : 8, + "QuantityPerUnit" : "24 - 250 g jars", + "UnitPrice" : 19.0000, + "UnitsInStock" : 112, + "UnitsOnOrder" : 0, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 37, + "ProductName" : "Gravad lax", + "SupplierID" : 17, + "CategoryID" : 8, + "QuantityPerUnit" : "12 - 500 g pkgs.", + "UnitPrice" : 26.0000, + "UnitsInStock" : 11, + "UnitsOnOrder" : 50, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 38, + "ProductName" : "Côte de Blaye", + "SupplierID" : 18, + "CategoryID" : 1, + "QuantityPerUnit" : "12 - 75 cl bottles", + "UnitPrice" : 263.5000, + "UnitsInStock" : 17, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 39, + "ProductName" : "Chartreuse verte", + "SupplierID" : 18, + "CategoryID" : 1, + "QuantityPerUnit" : "750 cc per bottle", + "UnitPrice" : 18.0000, + "UnitsInStock" : 69, + "UnitsOnOrder" : 0, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 40, + "ProductName" : "Boston Crab Meat", + "SupplierID" : 19, + "CategoryID" : 8, + "QuantityPerUnit" : "24 - 4 oz tins", + "UnitPrice" : 18.4000, + "UnitsInStock" : 123, + "UnitsOnOrder" : 0, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 41, + "ProductName" : "Jack's New England Clam Chowder", + "SupplierID" : 19, + "CategoryID" : 8, + "QuantityPerUnit" : "12 - 12 oz cans", + "UnitPrice" : 9.6500, + "UnitsInStock" : 85, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 42, + "ProductName" : "Singaporean Hokkien Fried Mee", + "SupplierID" : 20, + "CategoryID" : 5, + "QuantityPerUnit" : "32 - 1 kg pkgs.", + "UnitPrice" : 14.0000, + "UnitsInStock" : 26, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 43, + "ProductName" : "Ipoh Coffee", + "SupplierID" : 20, + "CategoryID" : 1, + "QuantityPerUnit" : "16 - 500 g tins", + "UnitPrice" : 46.0000, + "UnitsInStock" : 17, + "UnitsOnOrder" : 10, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 44, + "ProductName" : "Gula Malacca", + "SupplierID" : 20, + "CategoryID" : 2, + "QuantityPerUnit" : "20 - 2 kg bags", + "UnitPrice" : 19.4500, + "UnitsInStock" : 27, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 45, + "ProductName" : "Rogede sild", + "SupplierID" : 21, + "CategoryID" : 8, + "QuantityPerUnit" : "1k pkg.", + "UnitPrice" : 9.5000, + "UnitsInStock" : 5, + "UnitsOnOrder" : 70, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 46, + "ProductName" : "Spegesild", + "SupplierID" : 21, + "CategoryID" : 8, + "QuantityPerUnit" : "4 - 450 g glasses", + "UnitPrice" : 12.0000, + "UnitsInStock" : 95, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 47, + "ProductName" : "Zaanse koeken", + "SupplierID" : 22, + "CategoryID" : 3, + "QuantityPerUnit" : "10 - 4 oz boxes", + "UnitPrice" : 9.5000, + "UnitsInStock" : 36, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 48, + "ProductName" : "Chocolade", + "SupplierID" : 22, + "CategoryID" : 3, + "QuantityPerUnit" : "10 pkgs.", + "UnitPrice" : 12.7500, + "UnitsInStock" : 15, + "UnitsOnOrder" : 70, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 49, + "ProductName" : "Maxilaku", + "SupplierID" : 23, + "CategoryID" : 3, + "QuantityPerUnit" : "24 - 50 g pkgs.", + "UnitPrice" : 20.0000, + "UnitsInStock" : 10, + "UnitsOnOrder" : 60, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 50, + "ProductName" : "Valkoinen suklaa", + "SupplierID" : 23, + "CategoryID" : 3, + "QuantityPerUnit" : "12 - 100 g bars", + "UnitPrice" : 16.2500, + "UnitsInStock" : 65, + "UnitsOnOrder" : 0, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 51, + "ProductName" : "Manjimup Dried Apples", + "SupplierID" : 24, + "CategoryID" : 7, + "QuantityPerUnit" : "50 - 300 g pkgs.", + "UnitPrice" : 53.0000, + "UnitsInStock" : 20, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 7, + "CategoryName" : "Produce", + "Description" : "Dried fruit and bean curd" + } +}, { + "ProductID" : 52, + "ProductName" : "Filo Mix", + "SupplierID" : 24, + "CategoryID" : 5, + "QuantityPerUnit" : "16 - 2 kg boxes", + "UnitPrice" : 7.0000, + "UnitsInStock" : 38, + "UnitsOnOrder" : 0, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 53, + "ProductName" : "Perth Pasties", + "SupplierID" : 24, + "CategoryID" : 6, + "QuantityPerUnit" : "48 pieces", + "UnitPrice" : 32.8000, + "UnitsInStock" : 0, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : true, + "Category" : { + "CategoryID" : 6, + "CategoryName" : "Meat/Poultry", + "Description" : "Prepared meats" + } +}, { + "ProductID" : 54, + "ProductName" : "Tourtière", + "SupplierID" : 25, + "CategoryID" : 6, + "QuantityPerUnit" : "16 pies", + "UnitPrice" : 7.4500, + "UnitsInStock" : 21, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 6, + "CategoryName" : "Meat/Poultry", + "Description" : "Prepared meats" + } +}, { + "ProductID" : 55, + "ProductName" : "Pâté chinois", + "SupplierID" : 25, + "CategoryID" : 6, + "QuantityPerUnit" : "24 boxes x 2 pies", + "UnitPrice" : 24.0000, + "UnitsInStock" : 115, + "UnitsOnOrder" : 0, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 6, + "CategoryName" : "Meat/Poultry", + "Description" : "Prepared meats" + } +}, { + "ProductID" : 56, + "ProductName" : "Gnocchi di nonna Alice", + "SupplierID" : 26, + "CategoryID" : 5, + "QuantityPerUnit" : "24 - 250 g pkgs.", + "UnitPrice" : 38.0000, + "UnitsInStock" : 21, + "UnitsOnOrder" : 10, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 57, + "ProductName" : "Ravioli Angelo", + "SupplierID" : 26, + "CategoryID" : 5, + "QuantityPerUnit" : "24 - 250 g pkgs.", + "UnitPrice" : 19.5000, + "UnitsInStock" : 36, + "UnitsOnOrder" : 0, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 58, + "ProductName" : "Escargots de Bourgogne", + "SupplierID" : 27, + "CategoryID" : 8, + "QuantityPerUnit" : "24 pieces", + "UnitPrice" : 13.2500, + "UnitsInStock" : 62, + "UnitsOnOrder" : 0, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 59, + "ProductName" : "Raclette Courdavault", + "SupplierID" : 28, + "CategoryID" : 4, + "QuantityPerUnit" : "5 kg pkg.", + "UnitPrice" : 55.0000, + "UnitsInStock" : 79, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 60, + "ProductName" : "Camembert Pierrot", + "SupplierID" : 28, + "CategoryID" : 4, + "QuantityPerUnit" : "15 - 300 g rounds", + "UnitPrice" : 34.0000, + "UnitsInStock" : 19, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 61, + "ProductName" : "Sirop d'érable", + "SupplierID" : 29, + "CategoryID" : 2, + "QuantityPerUnit" : "24 - 500 ml bottles", + "UnitPrice" : 28.5000, + "UnitsInStock" : 113, + "UnitsOnOrder" : 0, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 62, + "ProductName" : "Tarte au sucre", + "SupplierID" : 29, + "CategoryID" : 3, + "QuantityPerUnit" : "48 pies", + "UnitPrice" : 49.3000, + "UnitsInStock" : 17, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 63, + "ProductName" : "Vegie-spread", + "SupplierID" : 7, + "CategoryID" : 2, + "QuantityPerUnit" : "15 - 625 g jars", + "UnitPrice" : 43.9000, + "UnitsInStock" : 24, + "UnitsOnOrder" : 0, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 64, + "ProductName" : "Wimmers gute Semmelknödel", + "SupplierID" : 12, + "CategoryID" : 5, + "QuantityPerUnit" : "20 bags x 4 pieces", + "UnitPrice" : 33.2500, + "UnitsInStock" : 22, + "UnitsOnOrder" : 80, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 5, + "CategoryName" : "Grains/Cereals", + "Description" : "Breads, crackers, pasta, and cereal" + } +}, { + "ProductID" : 65, + "ProductName" : "Louisiana Fiery Hot Pepper Sauce", + "SupplierID" : 2, + "CategoryID" : 2, + "QuantityPerUnit" : "32 - 8 oz bottles", + "UnitPrice" : 21.0500, + "UnitsInStock" : 76, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 66, + "ProductName" : "Louisiana Hot Spiced Okra", + "SupplierID" : 2, + "CategoryID" : 2, + "QuantityPerUnit" : "24 - 8 oz jars", + "UnitPrice" : 17.0000, + "UnitsInStock" : 4, + "UnitsOnOrder" : 100, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}, { + "ProductID" : 67, + "ProductName" : "Laughing Lumberjack Lager", + "SupplierID" : 16, + "CategoryID" : 1, + "QuantityPerUnit" : "24 - 12 oz bottles", + "UnitPrice" : 14.0000, + "UnitsInStock" : 52, + "UnitsOnOrder" : 0, + "ReorderLevel" : 10, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 68, + "ProductName" : "Scottish Longbreads", + "SupplierID" : 8, + "CategoryID" : 3, + "QuantityPerUnit" : "10 boxes x 8 pieces", + "UnitPrice" : 12.5000, + "UnitsInStock" : 6, + "UnitsOnOrder" : 10, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 3, + "CategoryName" : "Confections", + "Description" : "Desserts, candies, and sweet breads" + } +}, { + "ProductID" : 69, + "ProductName" : "Gudbrandsdalsost", + "SupplierID" : 15, + "CategoryID" : 4, + "QuantityPerUnit" : "10 kg pkg.", + "UnitPrice" : 36.0000, + "UnitsInStock" : 26, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 70, + "ProductName" : "Outback Lager", + "SupplierID" : 7, + "CategoryID" : 1, + "QuantityPerUnit" : "24 - 355 ml bottles", + "UnitPrice" : 15.0000, + "UnitsInStock" : 15, + "UnitsOnOrder" : 10, + "ReorderLevel" : 30, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 71, + "ProductName" : "Flotemysost", + "SupplierID" : 15, + "CategoryID" : 4, + "QuantityPerUnit" : "10 - 500 g pkgs.", + "UnitPrice" : 21.5000, + "UnitsInStock" : 26, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 72, + "ProductName" : "Mozzarella di Giovanni", + "SupplierID" : 14, + "CategoryID" : 4, + "QuantityPerUnit" : "24 - 200 g pkgs.", + "UnitPrice" : 34.8000, + "UnitsInStock" : 14, + "UnitsOnOrder" : 0, + "ReorderLevel" : 0, + "Discontinued" : false, + "Category" : { + "CategoryID" : 4, + "CategoryName" : "Dairy Products", + "Description" : "Cheeses" + } +}, { + "ProductID" : 73, + "ProductName" : "Röd Kaviar", + "SupplierID" : 17, + "CategoryID" : 8, + "QuantityPerUnit" : "24 - 150 g jars", + "UnitPrice" : 15.0000, + "UnitsInStock" : 101, + "UnitsOnOrder" : 0, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 8, + "CategoryName" : "Seafood", + "Description" : "Seaweed and fish" + } +}, { + "ProductID" : 74, + "ProductName" : "Longlife Tofu", + "SupplierID" : 4, + "CategoryID" : 7, + "QuantityPerUnit" : "5 kg pkg.", + "UnitPrice" : 10.0000, + "UnitsInStock" : 4, + "UnitsOnOrder" : 20, + "ReorderLevel" : 5, + "Discontinued" : false, + "Category" : { + "CategoryID" : 7, + "CategoryName" : "Produce", + "Description" : "Dried fruit and bean curd" + } +}, { + "ProductID" : 75, + "ProductName" : "Rhönbräu Klosterbier", + "SupplierID" : 12, + "CategoryID" : 1, + "QuantityPerUnit" : "24 - 0.5 l bottles", + "UnitPrice" : 7.7500, + "UnitsInStock" : 125, + "UnitsOnOrder" : 0, + "ReorderLevel" : 25, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 76, + "ProductName" : "Lakkalikööri", + "SupplierID" : 23, + "CategoryID" : 1, + "QuantityPerUnit" : "500 ml", + "UnitPrice" : 18.0000, + "UnitsInStock" : 57, + "UnitsOnOrder" : 0, + "ReorderLevel" : 20, + "Discontinued" : false, + "Category" : { + "CategoryID" : 1, + "CategoryName" : "Beverages", + "Description" : "Soft drinks, coffees, teas, beers, and ales" + } +}, { + "ProductID" : 77, + "ProductName" : "Original Frankfurter grüne Soße", + "SupplierID" : 12, + "CategoryID" : 2, + "QuantityPerUnit" : "12 boxes", + "UnitPrice" : 13.0000, + "UnitsInStock" : 32, + "UnitsOnOrder" : 0, + "ReorderLevel" : 15, + "Discontinued" : false, + "Category" : { + "CategoryID" : 2, + "CategoryName" : "Condiments", + "Description" : "Sweet and savory sauces, relishes, spreads, and seasonings" + } +}] \ No newline at end of file diff --git a/examples/kendo-react-redux-form/src/index.css b/examples/kendo-react-redux-form/src/index.css new file mode 100644 index 00000000..b4cc7250 --- /dev/null +++ b/examples/kendo-react-redux-form/src/index.css @@ -0,0 +1,5 @@ +body { + margin: 0; + padding: 0; + font-family: sans-serif; +} diff --git a/examples/kendo-react-redux-form/src/index.js b/examples/kendo-react-redux-form/src/index.js new file mode 100644 index 00000000..b785c2d9 --- /dev/null +++ b/examples/kendo-react-redux-form/src/index.js @@ -0,0 +1,15 @@ +import React from 'react'; +import './index.css'; +import App from './App'; +import { render } from 'react-dom' +import { Provider } from 'react-redux' +import { store } from './reducers/index' +  + +  +render( + + + , + document.getElementById('root') +) diff --git a/examples/kendo-react-redux-form/src/logo.svg b/examples/kendo-react-redux-form/src/logo.svg new file mode 100644 index 00000000..6b60c104 --- /dev/null +++ b/examples/kendo-react-redux-form/src/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/kendo-react-redux-form/src/reducers/index.js b/examples/kendo-react-redux-form/src/reducers/index.js new file mode 100644 index 00000000..ce0950ff --- /dev/null +++ b/examples/kendo-react-redux-form/src/reducers/index.js @@ -0,0 +1,7 @@ +import { combineReducers, createStore, applyMiddleware } from 'redux'; +import { composeWithDevTools } from 'redux-devtools-extension' +import { reducer as form } from 'redux-form' +import thunk from 'redux-thunk' + +const orderForm = combineReducers({ form }); +export const store = createStore(orderForm, composeWithDevTools(applyMiddleware(thunk))) diff --git a/examples/kendo-react-redux-form/src/registerServiceWorker.js b/examples/kendo-react-redux-form/src/registerServiceWorker.js new file mode 100644 index 00000000..a3e6c0cf --- /dev/null +++ b/examples/kendo-react-redux-form/src/registerServiceWorker.js @@ -0,0 +1,117 @@ +// In production, we register a service worker to serve assets from local cache. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on the "N+1" visit to a page, since previously +// cached resources are updated in the background. + +// To learn more about the benefits of this model, read https://goo.gl/KwvDNy. +// This link also includes instructions on opting out of this behavior. + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + +export default function register() { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Lets check if a service worker still exists or not. + checkValidServiceWorker(swUrl); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://goo.gl/SC7cgQ' + ); + }); + } else { + // Is not local host. Just register service worker + registerValidSW(swUrl); + } + }); + } +} + +function registerValidSW(swUrl) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the old content will have been purged and + // the fresh content will have been added to the cache. + // It's the perfect time to display a "New content is + // available; please refresh." message in your web app. + console.log('New content is available; please refresh.'); + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + if ( + response.status === 404 || + response.headers.get('content-type').indexOf('javascript') === -1 + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +} From bb0a517b29e92679adfb7e064e63621c6d03ba96 Mon Sep 17 00:00:00 2001 From: Ina Glushkova Date: Thu, 29 May 2025 14:38:05 +0300 Subject: [PATCH 06/11] chore: bump versions and update kendo-react-redux-form --- examples/kendo-react-redux-form/package.json | 59 +++++++----- .../kendo-react-redux-form/src/App.test.js | 7 +- .../src/components/FormGrid.js | 90 +++++-------------- .../src/components/ReduxForm.js | 2 +- examples/kendo-react-redux-form/src/index.js | 18 ++-- 5 files changed, 75 insertions(+), 101 deletions(-) diff --git a/examples/kendo-react-redux-form/package.json b/examples/kendo-react-redux-form/package.json index 098bb091..cabd7601 100644 --- a/examples/kendo-react-redux-form/package.json +++ b/examples/kendo-react-redux-form/package.json @@ -3,33 +3,52 @@ "version": "0.1.0", "private": true, "dependencies": { - "@progress/kendo-data-query": "^1.4.0", - "@progress/kendo-drawing": "^1.5.7", - "@progress/kendo-react-buttons": "^2.0.0", - "@progress/kendo-react-dateinputs": "^2.0.0", - "@progress/kendo-react-dropdowns": "^2.0.0", - "@progress/kendo-react-animation": "^2.0.0", - "@progress/kendo-react-popup": "^2.0.0", - "@progress/kendo-react-grid": "^2.0.0", - "@progress/kendo-react-inputs": "^2.0.0", - "@progress/kendo-react-intl": "^2.0.0", - "@progress/kendo-react-pdf": "^2.0.0", - "@progress/kendo-theme-default": "^2.54.1", + "@progress/kendo-licensing": "^1.6.0", + "@progress/kendo-data-query": "^1.7.1", + "@progress/kendo-intl": "^3.1.2", + "@progress/kendo-drawing": "^1.21.2", + "@progress/kendo-date-math": "^1.5.14", + "@progress/kendo-react-buttons": "^11.0.0", + "@progress/kendo-react-dateinputs": "^11.0.0", + "@progress/kendo-react-dropdowns": "^11.0.0", + "@progress/kendo-react-animation": "^11.0.0", + "@progress/kendo-react-popup": "^11.0.0", + "@progress/kendo-react-grid": "^11.0.0", + "@progress/kendo-react-inputs": "^11.0.0", + "@progress/kendo-react-intl": "^11.0.0", + "@progress/kendo-react-pdf": "^11.0.0", + "@progress/kendo-react-common": "^11.0.0", + "@progress/kendo-react-data-tools": "^11.0.0", + "@progress/kendo-react-layout": "^11.0.0", + "@progress/kendo-react-indicators": "^11.0.0", + "@progress/kendo-theme-default": "^11.0.2", "popper.js": "^1.14.3", "prop-types": "^15.6.2", - "react": "^16.4.1", - "react-dom": "^16.4.1", - "react-redux": "^5.0.7", - "react-scripts": "^1.1.4", - "redux": "^4.0.0", - "redux-devtools-extension": "^2.13.5", - "redux-form": "^7.4.2", - "redux-thunk": "^2.3.0" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-redux": "^8.1.3", + "react-scripts": "^5.0.1", + "redux": "^4.2.1", + "redux-devtools-extension": "^2.13.9", + "redux-form": "^8.3.10", + "redux-thunk": "^2.4.2" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] } } diff --git a/examples/kendo-react-redux-form/src/App.test.js b/examples/kendo-react-redux-form/src/App.test.js index a754b201..1062a245 100644 --- a/examples/kendo-react-redux-form/src/App.test.js +++ b/examples/kendo-react-redux-form/src/App.test.js @@ -1,9 +1,10 @@ import React from 'react'; -import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; import App from './App'; it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(, div); - ReactDOM.unmountComponentAtNode(div); + const root = createRoot(div); + root.render(); + root.unmount(); }); diff --git a/examples/kendo-react-redux-form/src/components/FormGrid.js b/examples/kendo-react-redux-form/src/components/FormGrid.js index 6e230a38..ef4d25a2 100644 --- a/examples/kendo-react-redux-form/src/components/FormGrid.js +++ b/examples/kendo-react-redux-form/src/components/FormGrid.js @@ -1,11 +1,10 @@ import React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { Grid, GridColumn, GridToolbar } from '@progress/kendo-react-grid'; -import { Button } from '@progress/kendo-react-buttons'; -import { Field, change as formChange } from 'redux-form'; -import RemoveCell from './RemoveCell'; -import { CellEditor, INDEXFIELD } from './Editors'; +import { Grid, GridColumn as Column, GridToolbar, GridToolbarSort, GridToolbarFilter } from '@progress/kendo-react-grid'; +import { gearIcon } from '@progress/kendo-svg-icons'; +import { change as formChange } from 'redux-form'; +import { INDEXFIELD } from './Editors'; const mapStateToProps = () => { return {}; @@ -18,78 +17,33 @@ const mapDispatchToProps = (dispatch) => { }; class GridHOC extends React.Component { - onAdd = (e) => { - const { fields, defaultValue } = this.props; - - e.preventDefault(); - fields.unshift(defaultValue); - } - - onRemove = (cellProps) => { - const { fields } = this.props; - - if (window.confirm('Are you sure you want to delete this item?')) { - fields.remove(cellProps.dataItem[INDEXFIELD]); - } - } - - renderCommandCell = (cellProps) => { - return - { this.onRemove(cellProps) }} - /> - ; - } - - renderCellEditor = (cellInfo) => { - const columnOptions = this.props.columns.find(col => col.field === cellInfo.field); - - return CellEditor(cellInfo, this.props.fields.name, columnOptions); - } - - renderColumns = (columns) => { - return [...columns, { - field: '', - cell: this.renderCommandCell - }].map(column => { - const { field, cell, ...rest } = column; - - return - }); - } - render() { - const { columns, fields } = this.props; - - // Use this approach till index is made build in - const data = fields.getAll().map((item, index) => { + const data = this.props.fields.getAll().map((item, index) => { return { ...item, + Product: item.Product.ProductName, [INDEXFIELD]: index }; }); return ( - - - - { - this.renderColumns(columns) - } - + style={{ minWidth: '500px' }} + adaptive={true} + dataItemKey="index" + data={data} + autoProcessData={true} + navigatable={true} + sortable={{ mode: 'multiple' }} + > + + + + + + + + ); } } diff --git a/examples/kendo-react-redux-form/src/components/ReduxForm.js b/examples/kendo-react-redux-form/src/components/ReduxForm.js index 08374c78..4850e1af 100644 --- a/examples/kendo-react-redux-form/src/components/ReduxForm.js +++ b/examples/kendo-react-redux-form/src/components/ReduxForm.js @@ -87,7 +87,7 @@ let ReduxProductsForm = props => { ]} />
- +   + +
+ + + )} /> + + + ) + } +} + +export default FinalForm \ No newline at end of file diff --git a/examples/kendo-react-final-form/src/index.css b/examples/kendo-react-final-form/src/index.css new file mode 100644 index 00000000..78a42d95 --- /dev/null +++ b/examples/kendo-react-final-form/src/index.css @@ -0,0 +1,9 @@ +body { + margin: 0; + padding: 0; + font-family: sans-serif; +} + +form { + width: 500px; +} diff --git a/examples/kendo-react-final-form/src/index.js b/examples/kendo-react-final-form/src/index.js new file mode 100644 index 00000000..fae3e350 --- /dev/null +++ b/examples/kendo-react-final-form/src/index.js @@ -0,0 +1,8 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import './index.css'; +import App from './App'; +import registerServiceWorker from './registerServiceWorker'; + +ReactDOM.render(, document.getElementById('root')); +registerServiceWorker(); diff --git a/examples/kendo-react-final-form/src/logo.svg b/examples/kendo-react-final-form/src/logo.svg new file mode 100644 index 00000000..6b60c104 --- /dev/null +++ b/examples/kendo-react-final-form/src/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/kendo-react-final-form/src/registerServiceWorker.js b/examples/kendo-react-final-form/src/registerServiceWorker.js new file mode 100644 index 00000000..a3e6c0cf --- /dev/null +++ b/examples/kendo-react-final-form/src/registerServiceWorker.js @@ -0,0 +1,117 @@ +// In production, we register a service worker to serve assets from local cache. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on the "N+1" visit to a page, since previously +// cached resources are updated in the background. + +// To learn more about the benefits of this model, read https://goo.gl/KwvDNy. +// This link also includes instructions on opting out of this behavior. + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + +export default function register() { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Lets check if a service worker still exists or not. + checkValidServiceWorker(swUrl); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://goo.gl/SC7cgQ' + ); + }); + } else { + // Is not local host. Just register service worker + registerValidSW(swUrl); + } + }); + } +} + +function registerValidSW(swUrl) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the old content will have been purged and + // the fresh content will have been added to the cache. + // It's the perfect time to display a "New content is + // available; please refresh." message in your web app. + console.log('New content is available; please refresh.'); + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + if ( + response.status === 404 || + response.headers.get('content-type').indexOf('javascript') === -1 + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +} From 376378ea9c381f3bb277b8b593a91e20c3daa18e Mon Sep 17 00:00:00 2001 From: Ina Glushkova Date: Thu, 29 May 2025 16:56:34 +0300 Subject: [PATCH 08/11] chore: bump versions and update kendo-react-final-form-app --- examples/kendo-react-final-form/package.json | 34 +++++++++++++------ .../kendo-react-final-form/src/App.test.js | 9 ++--- .../kendo-react-final-form/src/FinalForm.js | 5 +-- examples/kendo-react-final-form/src/index.js | 12 +++++-- 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/examples/kendo-react-final-form/package.json b/examples/kendo-react-final-form/package.json index eb201cc7..6502923d 100644 --- a/examples/kendo-react-final-form/package.json +++ b/examples/kendo-react-final-form/package.json @@ -3,18 +3,30 @@ "version": "0.1.0", "private": true, "dependencies": { - "@progress/kendo-react-dateinputs": "^2.0.0", - "@progress/kendo-react-dropdowns": "^2.0.0", - "@progress/kendo-react-inputs": "^2.0.0", - "@progress/kendo-react-intl": "^2.0.0", - "@progress/kendo-react-buttons": "^2.0.0", - "@progress/kendo-theme-default": "^2.50.0", - "prop-types": "^15.6.1", + "@progress/kendo-licensing": "^1.5.1", + "@progress/kendo-date-math": "^1.5.14", + "@progress/kendo-inputs-common": "^3.1.1", + "@progress/kendo-intl": "^3.1.2", + "@progress/kendo-drawing": "^1.21.2", + "@progress/kendo-dateinputs-common": "^0.4.5", + "@progress/kendo-react-dateinputs": "^11.0.0", + "@progress/kendo-react-dropdowns": "^11.0.0", + "@progress/kendo-react-inputs": "^11.0.0", + "@progress/kendo-react-intl": "^11.0.0", + "@progress/kendo-react-buttons": "^11.0.0", + "@progress/kendo-react-common": "^11.0.0", + "@progress/kendo-react-layout": "^11.0.0", + "@progress/kendo-react-labels": "^11.0.0", + "@progress/kendo-react-popup": "^11.0.0", + "@progress/kendo-react-treeview": "^11.0.0", + "@progress/kendo-react-dialogs": "^11.0.0", + "@progress/kendo-theme-default": "^11.0.2", + "prop-types": "^15.6.2", "final-form": "^4.4.0", - "react": "^16.3.1", - "react-dom": "^16.3.1", - "react-final-form": "^3.1.5", - "react-scripts": "1.1.4" + "react": "^18", + "react-dom": "^18", + "react-final-form": "^6.5.9", + "react-scripts": "^5.0.1" }, "scripts": { "start": "react-scripts start", diff --git a/examples/kendo-react-final-form/src/App.test.js b/examples/kendo-react-final-form/src/App.test.js index a754b201..67e72c10 100644 --- a/examples/kendo-react-final-form/src/App.test.js +++ b/examples/kendo-react-final-form/src/App.test.js @@ -1,9 +1,6 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(, div); - ReactDOM.unmountComponentAtNode(div); + const root = createRoot(div); + root.render(); + root.unmount(); }); diff --git a/examples/kendo-react-final-form/src/FinalForm.js b/examples/kendo-react-final-form/src/FinalForm.js index f4946d98..2f801bac 100644 --- a/examples/kendo-react-final-form/src/FinalForm.js +++ b/examples/kendo-react-final-form/src/FinalForm.js @@ -88,7 +88,8 @@ class FinalForm extends Component { render() { return (
-
+
Flight Search
@@ -126,7 +127,7 @@ class FinalForm extends Component { offLabel={"NO"} label="Only direct flights" />
-   diff --git a/examples/kendo-react-final-form/src/index.js b/examples/kendo-react-final-form/src/index.js index fae3e350..ba371948 100644 --- a/examples/kendo-react-final-form/src/index.js +++ b/examples/kendo-react-final-form/src/index.js @@ -1,8 +1,16 @@ import React from 'react'; -import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; import './index.css'; import App from './App'; import registerServiceWorker from './registerServiceWorker'; -ReactDOM.render(, document.getElementById('root')); +const container = document.getElementById('root'); +const root = createRoot(container); + +root.render( + + + +); + registerServiceWorker(); From 56313e97c844da427ab18f31b279e76cbb96aa35 Mon Sep 17 00:00:00 2001 From: Ina Glushkova Date: Thu, 29 May 2025 17:00:47 +0300 Subject: [PATCH 09/11] chore: update package.json for redux-form app --- examples/kendo-react-redux-form/package.json | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/examples/kendo-react-redux-form/package.json b/examples/kendo-react-redux-form/package.json index cabd7601..63641a9a 100644 --- a/examples/kendo-react-redux-form/package.json +++ b/examples/kendo-react-redux-form/package.json @@ -38,17 +38,5 @@ "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] } } From 2052e1066a87e357ba126201190fb638cef9ef0c Mon Sep 17 00:00:00 2001 From: Ina Glushkova Date: Thu, 29 May 2025 17:14:34 +0300 Subject: [PATCH 10/11] chore: add kendo-react-graphql app --- examples/kendo-react-graphql/README.md | 28 ++ .../kendo-react-graphql/client/.gitignore | 27 ++ .../kendo-react-graphql/client/package.json | 32 +++ .../client/public/favicon.ico | Bin 0 -> 3870 bytes .../client/public/index.html | 42 +++ .../client/public/manifest.json | 15 ++ .../kendo-react-graphql/client/src/App.css | 27 ++ .../kendo-react-graphql/client/src/App.js | 51 ++++ .../client/src/App.test.js | 9 + .../client/src/components/GridContainer.jsx | 66 +++++ .../client/src/components/MyCommandCell.js | 18 ++ .../client/src/components/ProductForm.jsx | 128 +++++++++ .../kendo-react-graphql/client/src/index.css | 5 + .../kendo-react-graphql/client/src/index.js | 6 + .../kendo-react-graphql/client/src/logo.svg | 7 + .../client/src/queries/queries.js | 39 +++ .../kendo-react-graphql/server/.gitignore | 27 ++ examples/kendo-react-graphql/server/app.js | 19 ++ .../kendo-react-graphql/server/package.json | 18 ++ .../server/schema/schema.js | 252 ++++++++++++++++++ 20 files changed, 816 insertions(+) create mode 100644 examples/kendo-react-graphql/README.md create mode 100644 examples/kendo-react-graphql/client/.gitignore create mode 100644 examples/kendo-react-graphql/client/package.json create mode 100644 examples/kendo-react-graphql/client/public/favicon.ico create mode 100644 examples/kendo-react-graphql/client/public/index.html create mode 100644 examples/kendo-react-graphql/client/public/manifest.json create mode 100644 examples/kendo-react-graphql/client/src/App.css create mode 100644 examples/kendo-react-graphql/client/src/App.js create mode 100644 examples/kendo-react-graphql/client/src/App.test.js create mode 100644 examples/kendo-react-graphql/client/src/components/GridContainer.jsx create mode 100644 examples/kendo-react-graphql/client/src/components/MyCommandCell.js create mode 100644 examples/kendo-react-graphql/client/src/components/ProductForm.jsx create mode 100644 examples/kendo-react-graphql/client/src/index.css create mode 100644 examples/kendo-react-graphql/client/src/index.js create mode 100644 examples/kendo-react-graphql/client/src/logo.svg create mode 100644 examples/kendo-react-graphql/client/src/queries/queries.js create mode 100644 examples/kendo-react-graphql/server/.gitignore create mode 100644 examples/kendo-react-graphql/server/app.js create mode 100644 examples/kendo-react-graphql/server/package.json create mode 100644 examples/kendo-react-graphql/server/schema/schema.js diff --git a/examples/kendo-react-graphql/README.md b/examples/kendo-react-graphql/README.md new file mode 100644 index 00000000..4be1656c --- /dev/null +++ b/examples/kendo-react-graphql/README.md @@ -0,0 +1,28 @@ +# Kendo UI for React Grid Integration with GraphQL. + +> This application shows an example of how one can use editable Kendo UI for React Grid with GraphQL queries. + +## Build Setup + +### Server setup + +All of these commands have to be executed inside the server folder. + +```bash +# install dependencies +npm install +# start the server at localhost:4000 +node app.js +``` + +### Client setup + +All of these commands have to be executed inside the client folder. +Ensure that the server is running on port 4000 before starting the client application. + +```bash +# install dependencies +npm install +# serve with hot reload at localhost:3000 +npm start +``` diff --git a/examples/kendo-react-graphql/client/.gitignore b/examples/kendo-react-graphql/client/.gitignore new file mode 100644 index 00000000..aae8560f --- /dev/null +++ b/examples/kendo-react-graphql/client/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local +**/kendo-ui-license** + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# package-lock file +package-lock.json \ No newline at end of file diff --git a/examples/kendo-react-graphql/client/package.json b/examples/kendo-react-graphql/client/package.json new file mode 100644 index 00000000..c06f44cd --- /dev/null +++ b/examples/kendo-react-graphql/client/package.json @@ -0,0 +1,32 @@ +{ + "name": "client", + "version": "0.1.0", + "private": true, + "dependencies": { + "@progress/kendo-data-query": "^1.3.1", + "@progress/kendo-drawing": "^1.5.6", + "@progress/kendo-react-dateinputs": "^1.1.0", + "@progress/kendo-react-dropdowns": "^1.1.0", + "@progress/kendo-react-grid": "^1.1.0", + "@progress/kendo-react-inputs": "^1.1.0", + "@progress/kendo-react-intl": "^1.1.0", + "@progress/kendo-react-pdf": "^1.1.0", + "@progress/kendo-theme-default": "^2.54.0", + "apollo-boost": "^0.1.10", + "bootstrap": "^4.1.1", + "cldr-data": "^32.0.1", + "graphql": "^0.13.2", + "jquery": "^3.3.1", + "popper.js": "^1.14.3", + "react": "^16.4.1", + "react-apollo": "^2.1.6", + "react-dom": "^16.4.1", + "react-scripts": "1.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} diff --git a/examples/kendo-react-graphql/client/public/favicon.ico b/examples/kendo-react-graphql/client/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a GIT binary patch literal 3870 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ literal 0 HcmV?d00001 diff --git a/examples/kendo-react-graphql/client/public/index.html b/examples/kendo-react-graphql/client/public/index.html new file mode 100644 index 00000000..1fce2e95 --- /dev/null +++ b/examples/kendo-react-graphql/client/public/index.html @@ -0,0 +1,42 @@ + + + + + + + + + + + + React App + + + +
+ + + diff --git a/examples/kendo-react-graphql/client/public/manifest.json b/examples/kendo-react-graphql/client/public/manifest.json new file mode 100644 index 00000000..ef19ec24 --- /dev/null +++ b/examples/kendo-react-graphql/client/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": "./index.html", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/examples/kendo-react-graphql/client/src/App.css b/examples/kendo-react-graphql/client/src/App.css new file mode 100644 index 00000000..cae63171 --- /dev/null +++ b/examples/kendo-react-graphql/client/src/App.css @@ -0,0 +1,27 @@ +.header { + position: relative; +} +.header h5 { + color: #888888; + display: inline-block; + position: relative; + margin: 0 0 10px; + padding: 0 10px 0 0; + background: #fff; + text-transform: uppercase; +} +.header:before { + content: ""; + display: block; + position: absolute; + left: 20px; + right: 20px; + bottom: 18px; + border-top: 1px solid #e8ecef; +} +.k-grid { + margin-bottom: 20px; +} +.k-form fieldset { + border-top-width: 0; +} \ No newline at end of file diff --git a/examples/kendo-react-graphql/client/src/App.js b/examples/kendo-react-graphql/client/src/App.js new file mode 100644 index 00000000..e1c11964 --- /dev/null +++ b/examples/kendo-react-graphql/client/src/App.js @@ -0,0 +1,51 @@ +import React, { Component } from 'react'; +import '@progress/kendo-theme-default/dist/all.css'; +import 'bootstrap'; +import './App.css'; +import GridContainer from './components/GridContainer' +import ProductsForm from './components/ProductForm' +import ApolloClient from 'apollo-boost'; +import { ApolloProvider } from 'react-apollo'; + + +// apollo client setup +const client = new ApolloClient({ + uri: 'http://localhost:4000/graphql' +}) + +class App extends Component { + + constructor(props){ + super(props) + this.state = { + selectedItem: {}, + inEdit: false + } + } + + changeRowSelection = (row) =>{ + this.setState({ + inEdit: true, + selectedItem: row + }) + } + + addItem = () =>{ + this.setState({ + inEdit: false + }) + } + + render() { + return ( + +
+ + +
+
+ ); + } +} + +export default App; diff --git a/examples/kendo-react-graphql/client/src/App.test.js b/examples/kendo-react-graphql/client/src/App.test.js new file mode 100644 index 00000000..a754b201 --- /dev/null +++ b/examples/kendo-react-graphql/client/src/App.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/examples/kendo-react-graphql/client/src/components/GridContainer.jsx b/examples/kendo-react-graphql/client/src/components/GridContainer.jsx new file mode 100644 index 00000000..90e207da --- /dev/null +++ b/examples/kendo-react-graphql/client/src/components/GridContainer.jsx @@ -0,0 +1,66 @@ +import React, {Component} from 'react'; +import { Grid, GridColumn, GridToolbar } from '@progress/kendo-react-grid'; +import MyCommandCell from './MyCommandCell' +import { graphql, compose } from 'react-apollo'; +import { getProductsQuery, deleteProductMutation } from '../queries/queries'; + + +class GridContainer extends Component { + + constructor(props){ + super(props) + this.remove = this.remove.bind(this); + this.CommandCell = MyCommandCell(this.remove); + } + + remove(dataItem) { + this.props.deleteProductMutation({ + variables: { + ProductID: dataItem.ProductID, + }, + refetchQueries: [{ query: getProductsQuery }] + }); + } + + handleRowClick = (event) => { + this.props.changeRowSelection(event.dataItem) + } + + handleAddItem = () => { + this.props.addItem() + } + + render() { + return ( +
+
+
Data
+
+ + + + + + + + + + +
+ ); + } +} + +export default compose( + graphql(getProductsQuery, { name: "getProductsQuery" }), + graphql(deleteProductMutation, { name: "deleteProductMutation" }) +)(GridContainer); \ No newline at end of file diff --git a/examples/kendo-react-graphql/client/src/components/MyCommandCell.js b/examples/kendo-react-graphql/client/src/components/MyCommandCell.js new file mode 100644 index 00000000..bb2b150a --- /dev/null +++ b/examples/kendo-react-graphql/client/src/components/MyCommandCell.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { GridCell } from '@progress/kendo-react-grid'; + +export default function MyCommandCell(remove) { + return class extends GridCell { + render() { + return ( + + + + ); + } + } +}; \ No newline at end of file diff --git a/examples/kendo-react-graphql/client/src/components/ProductForm.jsx b/examples/kendo-react-graphql/client/src/components/ProductForm.jsx new file mode 100644 index 00000000..49d499fc --- /dev/null +++ b/examples/kendo-react-graphql/client/src/components/ProductForm.jsx @@ -0,0 +1,128 @@ +import React, {Component} from 'react'; +import { NumericTextBox } from '@progress/kendo-react-inputs'; +import { graphql, compose } from 'react-apollo'; +import { addProductMutation, getProductsQuery, updateProductMutation} from '../queries/queries'; + +class ProductsForm extends Component { + + constructor(props) { + super(props) + this.state = { + ProductName: "", + UnitPrice: 0, + UnitsInStock: 0, + internalUpdate: false + } + this.handleSubmit = this.handleSubmit.bind(this); + } + + static getDerivedStateFromProps(nextProps, state) { + if(state.internalUpdate){ + state.internalUpdate = false + return state + } + + if(nextProps.inEdit){ + return { + ProductName:nextProps.selectedItem.ProductName, + UnitPrice:nextProps.selectedItem.UnitPrice, + UnitsInStock:nextProps.selectedItem.UnitsInStock, + } + } + else{ + return { + ProductName:"", + UnitPrice:0, + UnitsInStock:0, + } + } + } + + handleSubmit = (e) => { + e.preventDefault() + + if(this.props.inEdit){ + this.props.updateProductMutation({ + variables: { + ProductID: this.props.selectedItem.ProductID, + ProductName: this.state.ProductName, + UnitPrice: this.state.UnitPrice, + UnitsInStock: this.state.UnitsInStock + }, + refetchQueries: [{ query: getProductsQuery }] + }); + } + else{ + this.props.addProductMutation({ + variables: { + ProductName: this.state.ProductName, + UnitPrice: this.state.UnitPrice, + UnitsInStock: this.state.UnitsInStock + }, + refetchQueries: [{ query: getProductsQuery }] + }); + } + this.props.addItem() + this.setState({ + ProductName: "", + UnitPrice: 0, + UnitsInStock: 0, + internalUpdate: true + }) + } + render() { + return ( +
+
+
Product
+
+
+
+ + + +
+
+ +
+
+
+ ) + } +} + +export default compose( + graphql(addProductMutation, {name: "addProductMutation"}), + graphql(getProductsQuery, { name: "getProductsQuery" }), + graphql(updateProductMutation, { name: "updateProductMutation" }) + +)(ProductsForm); \ No newline at end of file diff --git a/examples/kendo-react-graphql/client/src/index.css b/examples/kendo-react-graphql/client/src/index.css new file mode 100644 index 00000000..b4cc7250 --- /dev/null +++ b/examples/kendo-react-graphql/client/src/index.css @@ -0,0 +1,5 @@ +body { + margin: 0; + padding: 0; + font-family: sans-serif; +} diff --git a/examples/kendo-react-graphql/client/src/index.js b/examples/kendo-react-graphql/client/src/index.js new file mode 100644 index 00000000..395b7499 --- /dev/null +++ b/examples/kendo-react-graphql/client/src/index.js @@ -0,0 +1,6 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import './index.css'; +import App from './App'; + +ReactDOM.render(, document.getElementById('root')); diff --git a/examples/kendo-react-graphql/client/src/logo.svg b/examples/kendo-react-graphql/client/src/logo.svg new file mode 100644 index 00000000..6b60c104 --- /dev/null +++ b/examples/kendo-react-graphql/client/src/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/kendo-react-graphql/client/src/queries/queries.js b/examples/kendo-react-graphql/client/src/queries/queries.js new file mode 100644 index 00000000..7d4fdcb3 --- /dev/null +++ b/examples/kendo-react-graphql/client/src/queries/queries.js @@ -0,0 +1,39 @@ +import { gql } from 'apollo-boost'; + +const getProductsQuery = gql ` +{ + products { + ProductID + ProductName + UnitPrice + UnitsInStock + } +} +`; + +const addProductMutation = gql` + mutation AddProduct($ProductName: String!, $UnitPrice: Float!, $UnitsInStock: Float!){ + AddProduct(ProductName: $ProductName, UnitPrice: $UnitPrice, UnitsInStock: $UnitsInStock){ + ProductName + ProductID + } + } +`; + +const updateProductMutation = gql` + mutation UpdateProduct($ProductID: ID!, $ProductName: String! ,$UnitPrice: Float!, $UnitsInStock: Float!){ + UpdateProduct(ProductID: $ProductID, ProductName: $ProductName, UnitPrice: $UnitPrice, UnitsInStock: $UnitsInStock){ + ProductID + } + } +`; + +const deleteProductMutation = gql` + mutation DeleteProduct($ProductID: ID!){ + DeleteProduct(ProductID: $ProductID){ + ProductID + } + } +`; + +export { getProductsQuery, addProductMutation, deleteProductMutation, updateProductMutation }; \ No newline at end of file diff --git a/examples/kendo-react-graphql/server/.gitignore b/examples/kendo-react-graphql/server/.gitignore new file mode 100644 index 00000000..aae8560f --- /dev/null +++ b/examples/kendo-react-graphql/server/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local +**/kendo-ui-license** + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# package-lock file +package-lock.json \ No newline at end of file diff --git a/examples/kendo-react-graphql/server/app.js b/examples/kendo-react-graphql/server/app.js new file mode 100644 index 00000000..3f3b827b --- /dev/null +++ b/examples/kendo-react-graphql/server/app.js @@ -0,0 +1,19 @@ +const express = require('express'); +const graphqlHTTP = require('express-graphql'); +const schema = require('./schema/schema'); +const cors = require('cors') + +const app = express(); + + +// allow cross-origin requests +app.use(cors()); + +// bind express with graphql +app.use('/graphql', graphqlHTTP({ + schema +})); + +app.listen(4000, () => { + console.log('now listening for requests on port 4000'); +}); \ No newline at end of file diff --git a/examples/kendo-react-graphql/server/package.json b/examples/kendo-react-graphql/server/package.json new file mode 100644 index 00000000..2f5b31c8 --- /dev/null +++ b/examples/kendo-react-graphql/server/package.json @@ -0,0 +1,18 @@ +{ + "name": "kendo-graphql", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "cors": "^2.8.4", + "express": "^4.16.3", + "express-graphql": "^0.6.12", + "graphql": "^0.13.2", + "uuid": "^3.2.1" + } +} diff --git a/examples/kendo-react-graphql/server/schema/schema.js b/examples/kendo-react-graphql/server/schema/schema.js new file mode 100644 index 00000000..5545f926 --- /dev/null +++ b/examples/kendo-react-graphql/server/schema/schema.js @@ -0,0 +1,252 @@ +const graphql = require('graphql'); +const uuidv1 = require('uuid/v1'); + +const { GraphQLObjectType, + GraphQLString, + GraphQLSchema, + GraphQLID, + GraphQLFloat, + GraphQLList, + GraphQLNonNull } = graphql; + +// products data +var products = [{ + 'ProductID' : 1, + 'ProductName' : 'Chai', + 'SupplierID' : 1, + 'CategoryID' : 1, + 'QuantityPerUnit' : '10 boxes x 20 bags', + 'UnitPrice' : 18.0000, + 'UnitsInStock' : 39, + 'UnitsOnOrder' : 0, + 'ReorderLevel' : 10, + 'Discontinued' : false, + 'Category' : { + 'CategoryID' : 1, + 'CategoryName' : 'Beverages', + 'Description' : 'Soft drinks, coffees, teas, beers, and ales' + } +}, { + 'ProductID' : 2, + 'ProductName' : 'Chang', + 'SupplierID' : 1, + 'CategoryID' : 1, + 'QuantityPerUnit' : '24 - 12 oz bottles', + 'UnitPrice' : 19.0000, + 'UnitsInStock' : 17, + 'UnitsOnOrder' : 40, + 'ReorderLevel' : 25, + 'Discontinued' : false, + 'Category' : { + 'CategoryID' : 1, + 'CategoryName' : 'Beverages', + 'Description' : 'Soft drinks, coffees, teas, beers, and ales' + } +}, { + 'ProductID' : 3, + 'ProductName' : 'Aniseed Syrup', + 'SupplierID' : 1, + 'CategoryID' : 2, + 'QuantityPerUnit' : '12 - 550 ml bottles', + 'UnitPrice' : 10.0000, + 'UnitsInStock' : 13, + 'UnitsOnOrder' : 70, + 'ReorderLevel' : 25, + 'Discontinued' : false, + 'Category' : { + 'CategoryID' : 2, + 'CategoryName' : 'Condiments', + 'Description' : 'Sweet and savory sauces, relishes, spreads, and seasonings' + } +}, { + 'ProductID' : 4, + 'ProductName' : 'Chef Anton\'s Cajun Seasoning', + 'SupplierID' : 2, + 'CategoryID' : 2, + 'QuantityPerUnit' : '48 - 6 oz jars', + 'UnitPrice' : 22.0000, + 'UnitsInStock' : 53, + 'UnitsOnOrder' : 0, + 'ReorderLevel' : 0, + 'Discontinued' : false, + 'Category' : { + 'CategoryID' : 2, + 'CategoryName' : 'Condiments', + 'Description' : 'Sweet and savory sauces, relishes, spreads, and seasonings' + } +}, { + 'ProductID' : 5, + 'ProductName' : 'Chef Anton\'s Gumbo Mix', + 'SupplierID' : 2, + 'CategoryID' : 2, + 'QuantityPerUnit' : '36 boxes', + 'UnitPrice' : 21.3500, + 'UnitsInStock' : 0, + 'UnitsOnOrder' : 0, + 'ReorderLevel' : 0, + 'Discontinued' : true, + 'Category' : { + 'CategoryID' : 2, + 'CategoryName' : 'Condiments', + 'Description' : 'Sweet and savory sauces, relishes, spreads, and seasonings' + } +}, { + 'ProductID' : 6, + 'ProductName' : 'Grandma\'s Boysenberry Spread', + 'SupplierID' : 3, + 'CategoryID' : 2, + 'QuantityPerUnit' : '12 - 8 oz jars', + 'UnitPrice' : 25.0000, + 'UnitsInStock' : 120, + 'UnitsOnOrder' : 0, + 'ReorderLevel' : 25, + 'Discontinued' : false, + 'Category' : { + 'CategoryID' : 2, + 'CategoryName' : 'Condiments', + 'Description' : 'Sweet and savory sauces, relishes, spreads, and seasonings' + } +}, { + 'ProductID' : 7, + 'ProductName' : 'Uncle Bob\'s Organic Dried Pears', + 'SupplierID' : 3, + 'CategoryID' : 7, + 'QuantityPerUnit' : '12 - 1 lb pkgs.', + 'UnitPrice' : 30.0000, + 'UnitsInStock' : 15, + 'UnitsOnOrder' : 0, + 'ReorderLevel' : 10, + 'Discontinued' : false, + 'Category' : { + 'CategoryID' : 7, + 'CategoryName' : 'Produce', + 'Description' : 'Dried fruit and bean curd' + } +}, { + 'ProductID' : 8, + 'ProductName' : 'Northwoods Cranberry Sauce', + 'SupplierID' : 3, + 'CategoryID' : 2, + 'QuantityPerUnit' : '12 - 12 oz jars', + 'UnitPrice' : 40.0000, + 'UnitsInStock' : 6, + 'UnitsOnOrder' : 0, + 'ReorderLevel' : 0, + 'Discontinued' : false, + 'Category' : { + 'CategoryID' : 2, + 'CategoryName' : 'Condiments', + 'Description' : 'Sweet and savory sauces, relishes, spreads, and seasonings' + } +}, { + 'ProductID' : 9, + 'ProductName' : 'Mishi Kobe Niku', + 'SupplierID' : 4, + 'CategoryID' : 6, + 'QuantityPerUnit' : '18 - 500 g pkgs.', + 'UnitPrice' : 97.0000, + 'UnitsInStock' : 29, + 'UnitsOnOrder' : 0, + 'ReorderLevel' : 0, + 'Discontinued' : true, + 'Category' : { + 'CategoryID' : 6, + 'CategoryName' : 'Meat/Poultry', + 'Description' : 'Prepared meats' + } +}, { + 'ProductID' : 10, + 'ProductName' : 'Ikura', + 'SupplierID' : 4, + 'CategoryID' : 8, + 'QuantityPerUnit' : '12 - 200 ml jars', + 'UnitPrice' : 31.0000, + 'UnitsInStock' : 31, + 'UnitsOnOrder' : 0, + 'ReorderLevel' : 0, + 'Discontinued' : false, + 'Category' : { + 'CategoryID' : 8, + 'CategoryName' : 'Seafood', + 'Description' : 'Seaweed and fish' + } +}]; + +const ProductType = new GraphQLObjectType({ + name: 'Product', + fields: ( ) => ({ + ProductID: { type: GraphQLID }, + ProductName: { type: GraphQLString }, + UnitPrice: { type: GraphQLFloat }, + UnitsInStock: { type: GraphQLFloat } + + }) +}); + +const RootQuery = new GraphQLObjectType({ + name: 'RootQueryType', + fields: { + products:{ + type: new GraphQLList(ProductType), + resolve(parent, args){ + return products + } + } + } +}); + +const Mutation = new GraphQLObjectType({ + name:'Mutation', + fields:{ + AddProduct:{ + type: ProductType, + args:{ + ProductName: { type: new GraphQLNonNull(GraphQLString) }, + UnitPrice: { type: new GraphQLNonNull(GraphQLFloat) }, + UnitsInStock: { type: new GraphQLNonNull(GraphQLFloat) }, + }, + resolve(parent, args) { + let newProduct = { + ProductID: uuidv1(), + ProductName: args.ProductName, + UnitsInStock: args.UnitsInStock, + UnitPrice: args.UnitPrice + } + products.unshift(newProduct) + return args + } + }, + DeleteProduct:{ + type: ProductType, + args:{ + ProductID: { type: new GraphQLNonNull(GraphQLID) } + }, + resolve(parent, args) { + let index = products.findIndex(product => product.ProductID == args.ProductID); + products.splice(index, 1) + return args.ProductID + } + }, + UpdateProduct:{ + type: ProductType, + args:{ + ProductID: { type: new GraphQLNonNull(GraphQLID) }, + ProductName: { type: new GraphQLNonNull(GraphQLString) }, + UnitPrice: { type: new GraphQLNonNull(GraphQLFloat) }, + UnitsInStock: { type: new GraphQLNonNull(GraphQLFloat) }, + }, + resolve(parent, args) { + let index = products.findIndex(product => product.ProductID == args.ProductID); + products[index].ProductName = args.ProductName + products[index].UnitsInStock = args.UnitsInStock + products[index].UnitPrice = args.UnitPrice + return args.ProductID + } + } + } +}) + +module.exports = new GraphQLSchema({ + query: RootQuery, + mutation: Mutation +}); \ No newline at end of file From b863b9fea9a815158349fd34ca543a04ea5f375b Mon Sep 17 00:00:00 2001 From: Ina Glushkova Date: Thu, 29 May 2025 22:20:42 +0300 Subject: [PATCH 11/11] chore: bump versions and update kendo-react-graphql app --- .../kendo-react-graphql/client/package.json | 30 ++++++++-------- .../kendo-react-graphql/client/src/App.js | 8 ++--- .../client/src/App.test.js | 7 ++-- .../client/src/components/GridContainer.jsx | 30 ++++++++-------- .../client/src/components/MyCommandCell.js | 35 +++++++++++-------- .../client/src/components/ProductForm.jsx | 29 ++++++++------- .../kendo-react-graphql/client/src/index.js | 6 ++-- .../client/src/queries/queries.js | 4 +-- examples/kendo-react-graphql/server/app.js | 14 ++++---- .../kendo-react-graphql/server/package.json | 10 +++--- .../server/schema/schema.js | 2 +- 11 files changed, 92 insertions(+), 83 deletions(-) diff --git a/examples/kendo-react-graphql/client/package.json b/examples/kendo-react-graphql/client/package.json index c06f44cd..478ac0d7 100644 --- a/examples/kendo-react-graphql/client/package.json +++ b/examples/kendo-react-graphql/client/package.json @@ -3,25 +3,25 @@ "version": "0.1.0", "private": true, "dependencies": { - "@progress/kendo-data-query": "^1.3.1", - "@progress/kendo-drawing": "^1.5.6", - "@progress/kendo-react-dateinputs": "^1.1.0", - "@progress/kendo-react-dropdowns": "^1.1.0", - "@progress/kendo-react-grid": "^1.1.0", - "@progress/kendo-react-inputs": "^1.1.0", - "@progress/kendo-react-intl": "^1.1.0", - "@progress/kendo-react-pdf": "^1.1.0", - "@progress/kendo-theme-default": "^2.54.0", - "apollo-boost": "^0.1.10", + "@apollo/client": "^3.5.10", + "@progress/kendo-data-query": "^1.7.1", + "@progress/kendo-drawing": "^1.21.2", + "@progress/kendo-react-dateinputs": "^11.0.0", + "@progress/kendo-react-dropdowns": "^11.0.0", + "@progress/kendo-react-grid": "^11.0.0", + "@progress/kendo-react-inputs": "^11.0.0", + "@progress/kendo-react-intl": "^11.0.0", + "@progress/kendo-react-pdf": "^11.0.0", + "@progress/kendo-theme-default": "^11.0.2", "bootstrap": "^4.1.1", "cldr-data": "^32.0.1", - "graphql": "^0.13.2", + "graphql": "^16.11.0", "jquery": "^3.3.1", + "lodash.flowright": "^3.5.0", "popper.js": "^1.14.3", - "react": "^16.4.1", - "react-apollo": "^2.1.6", - "react-dom": "^16.4.1", - "react-scripts": "1.1.4" + "react": "^18", + "react-dom": "^18", + "react-scripts": "^5.0.1" }, "scripts": { "start": "react-scripts start", diff --git a/examples/kendo-react-graphql/client/src/App.js b/examples/kendo-react-graphql/client/src/App.js index e1c11964..f87a11e3 100644 --- a/examples/kendo-react-graphql/client/src/App.js +++ b/examples/kendo-react-graphql/client/src/App.js @@ -4,14 +4,14 @@ import 'bootstrap'; import './App.css'; import GridContainer from './components/GridContainer' import ProductsForm from './components/ProductForm' -import ApolloClient from 'apollo-boost'; -import { ApolloProvider } from 'react-apollo'; +import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client'; // apollo client setup const client = new ApolloClient({ - uri: 'http://localhost:4000/graphql' -}) + uri: 'http://localhost:4000/graphql', + cache: new InMemoryCache(), +}); class App extends Component { diff --git a/examples/kendo-react-graphql/client/src/App.test.js b/examples/kendo-react-graphql/client/src/App.test.js index a754b201..1062a245 100644 --- a/examples/kendo-react-graphql/client/src/App.test.js +++ b/examples/kendo-react-graphql/client/src/App.test.js @@ -1,9 +1,10 @@ import React from 'react'; -import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; import App from './App'; it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(, div); - ReactDOM.unmountComponentAtNode(div); + const root = createRoot(div); + root.render(); + root.unmount(); }); diff --git a/examples/kendo-react-graphql/client/src/components/GridContainer.jsx b/examples/kendo-react-graphql/client/src/components/GridContainer.jsx index 90e207da..831223d2 100644 --- a/examples/kendo-react-graphql/client/src/components/GridContainer.jsx +++ b/examples/kendo-react-graphql/client/src/components/GridContainer.jsx @@ -1,9 +1,10 @@ import React, {Component} from 'react'; -import { Grid, GridColumn, GridToolbar } from '@progress/kendo-react-grid'; +import { Grid, GridColumn, GridToolbar, GridToolbarFilter, GridToolbarColumnsChooser } from '@progress/kendo-react-grid'; +import { gearIcon } from '@progress/kendo-svg-icons'; import MyCommandCell from './MyCommandCell' -import { graphql, compose } from 'react-apollo'; import { getProductsQuery, deleteProductMutation } from '../queries/queries'; - +import { graphql } from '@apollo/client/react/hoc'; +import flowRight from 'lodash.flowright'; class GridContainer extends Component { @@ -39,28 +40,27 @@ class GridContainer extends Component { - + + - + - + - +
); } } -export default compose( +export default flowRight( graphql(getProductsQuery, { name: "getProductsQuery" }), graphql(deleteProductMutation, { name: "deleteProductMutation" }) -)(GridContainer); \ No newline at end of file + )(GridContainer); \ No newline at end of file diff --git a/examples/kendo-react-graphql/client/src/components/MyCommandCell.js b/examples/kendo-react-graphql/client/src/components/MyCommandCell.js index bb2b150a..313c118a 100644 --- a/examples/kendo-react-graphql/client/src/components/MyCommandCell.js +++ b/examples/kendo-react-graphql/client/src/components/MyCommandCell.js @@ -1,18 +1,23 @@ import React from 'react'; -import { GridCell } from '@progress/kendo-react-grid'; +import { Button } from '@progress/kendo-react-buttons'; export default function MyCommandCell(remove) { - return class extends GridCell { - render() { - return ( - - - - ); - } - } -}; \ No newline at end of file + return function CommandCell(props) { + const { dataItem } = props; + + return ( + + + + ); + }; +} \ No newline at end of file diff --git a/examples/kendo-react-graphql/client/src/components/ProductForm.jsx b/examples/kendo-react-graphql/client/src/components/ProductForm.jsx index 49d499fc..3e499481 100644 --- a/examples/kendo-react-graphql/client/src/components/ProductForm.jsx +++ b/examples/kendo-react-graphql/client/src/components/ProductForm.jsx @@ -1,6 +1,10 @@ import React, {Component} from 'react'; import { NumericTextBox } from '@progress/kendo-react-inputs'; -import { graphql, compose } from 'react-apollo'; +import { Button } from '@progress/kendo-react-buttons'; +import { Input } from '@progress/kendo-react-inputs'; +import { Label } from '@progress/kendo-react-labels'; +import { graphql } from '@apollo/client/react/hoc'; +import flowRight from 'lodash.flowright'; import { addProductMutation, getProductsQuery, updateProductMutation} from '../queries/queries'; class ProductsForm extends Component { @@ -78,9 +82,9 @@ class ProductsForm extends Component {
-
- + }
@@ -120,9 +124,8 @@ class ProductsForm extends Component { } } -export default compose( - graphql(addProductMutation, {name: "addProductMutation"}), +export default flowRight( + graphql(addProductMutation, { name: "addProductMutation" }), graphql(getProductsQuery, { name: "getProductsQuery" }), graphql(updateProductMutation, { name: "updateProductMutation" }) - )(ProductsForm); \ No newline at end of file diff --git a/examples/kendo-react-graphql/client/src/index.js b/examples/kendo-react-graphql/client/src/index.js index 395b7499..ef4adc03 100644 --- a/examples/kendo-react-graphql/client/src/index.js +++ b/examples/kendo-react-graphql/client/src/index.js @@ -1,6 +1,8 @@ import React from 'react'; -import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; import './index.css'; import App from './App'; -ReactDOM.render(, document.getElementById('root')); +const container = document.getElementById('root'); +const root = createRoot(container); +root.render(); diff --git a/examples/kendo-react-graphql/client/src/queries/queries.js b/examples/kendo-react-graphql/client/src/queries/queries.js index 7d4fdcb3..e5646158 100644 --- a/examples/kendo-react-graphql/client/src/queries/queries.js +++ b/examples/kendo-react-graphql/client/src/queries/queries.js @@ -1,6 +1,6 @@ -import { gql } from 'apollo-boost'; +import { gql } from '@apollo/client'; -const getProductsQuery = gql ` +const getProductsQuery = gql` { products { ProductID diff --git a/examples/kendo-react-graphql/server/app.js b/examples/kendo-react-graphql/server/app.js index 3f3b827b..1f9e7ee8 100644 --- a/examples/kendo-react-graphql/server/app.js +++ b/examples/kendo-react-graphql/server/app.js @@ -1,19 +1,17 @@ const express = require('express'); -const graphqlHTTP = require('express-graphql'); +const { createHandler } = require('graphql-http/lib/use/express'); const schema = require('./schema/schema'); -const cors = require('cors') +const cors = require('cors'); const app = express(); - -// allow cross-origin requests app.use(cors()); -// bind express with graphql -app.use('/graphql', graphqlHTTP({ - schema +app.all('/graphql', createHandler({ + schema: schema, + graphiql: true, })); app.listen(4000, () => { - console.log('now listening for requests on port 4000'); + console.log('now listening for requests on port 4000'); }); \ No newline at end of file diff --git a/examples/kendo-react-graphql/server/package.json b/examples/kendo-react-graphql/server/package.json index 2f5b31c8..47a3047b 100644 --- a/examples/kendo-react-graphql/server/package.json +++ b/examples/kendo-react-graphql/server/package.json @@ -9,10 +9,10 @@ "author": "", "license": "ISC", "dependencies": { - "cors": "^2.8.4", - "express": "^4.16.3", - "express-graphql": "^0.6.12", - "graphql": "^0.13.2", - "uuid": "^3.2.1" + "cors": "^2.8.5", + "express": "^5.1.0", + "graphql-http": "^1.22.4", + "graphql": "^16.11.0", + "uuid": "^11.1.0" } } diff --git a/examples/kendo-react-graphql/server/schema/schema.js b/examples/kendo-react-graphql/server/schema/schema.js index 5545f926..13a2a067 100644 --- a/examples/kendo-react-graphql/server/schema/schema.js +++ b/examples/kendo-react-graphql/server/schema/schema.js @@ -1,5 +1,5 @@ const graphql = require('graphql'); -const uuidv1 = require('uuid/v1'); +const { v1: uuidv1 } = require('uuid'); const { GraphQLObjectType, GraphQLString,