Storybook: satu cerita menarik di setiap lembar gaya cerita lainnya?

Dibuat pada 21 Mar 2017  ·  67Komentar  ·  Sumber: storybookjs/storybook

Hai,

Saya mencoba mencari tahu mengapa saya mengalami masalah khusus ini. Saya secara dinamis memuat cerita seperti ini:

function loadStories() {
    const req = require.context('../components', true, /story.js$/);
    req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);

dan setiap file story.js memiliki file sass atau css masing-masing yang diimpor ke dalamnya (untuk tujuan gaya khusus cerita yang berada di luar komponen yang akan diimpor story.js , untuk menampilkan:

import './story.sass';

Saya memiliki sekitar 4 cerita sekarang dan ini adalah sumber dari setiap cerita iframe ... memuat setiap stylesheet:

screen shot 2017-03-21 at 9 56 22 am

Apakah ini perilaku normal ...?

-

demo

https://github.com/moimikey/729-single-stories-pulling-all-stylesheets

bug todo

Komentar yang paling membantu

Menempatkan akar bayangan di sekitar cerita akan langsung menyelesaikan masalah ini tanpa masalah kinerja @ndelangen

Semua 67 komentar

juga, webpack memancarkan semua cerita itu, jadi saya bertanya-tanya apakah itu cara saya mengkonfigurasi webpack?

screen shot 2017-03-21 at 11 27 10 am

Saya mencoba menggunakan dekorator juga, dan ini akhirnya menjadi setengah rusak, karena saya setidaknya dapat mengisolasi stylesheet tambahan ke cerita, tetapi ketika saya melintasi yang lain, gaya itu akan rusak kecuali saya melakukan penyegaran yang keras.

.addDecorator((getStory) => {
  require('./story.sass');
  return getStory();
})

@arunoda @mnmtanish

Menarik !, apakah Anda memiliki repo yang menunjukkan hal ini?

@ndelangen akan segera membuatnya

Masih berusaha mencari waktu untuk memeriksa ini ..

terima kasih; D itu aneh. Saya telah mencobanya, tetapi saya tidak yakin harus mulai dari mana.

Jadi yang menurut saya adalah kejadiannya adalah semua file CSS diambil oleh webpack style-loader dan baru saja dimasukkan ke dalam head. apakah mereka digunakan atau tidak.

Tapi bagaimana cara memperbaikinya?

Apa yang telah saya lakukan untuk CSS saya adalah menggunakan modul CSS. dan impor nama kelas yang dihasilkan ke JS saya. Bahkan jika semua CSS disuntikkan bersama ke dalam head, itu tidak masalah, karena dijamin menjadi kelas / penyeleksi yang unik.

Itu tidak benar-benar menyelesaikan masalah yang Anda alami. Tapi saya rasa ini adalah perilaku yang diinginkan dari style-loader.

ini benar. i (dan perusahaan saya) menggunakan css, tetapi kami melakukan pengembangan ulang kode yang sangat besar, jadi kami telah berbagi gaya, sehingga kombinasi dari styleName dan className . jadi yang akhirnya memengaruhi buku cerita adalah file css "orang luar" itu.

Saya akan melihat kode buku cerita lagi malam ini setelah saya minum bir dan mencari tahu bagaimana mungkin menyelesaikan ini, implementasi yang bijaksana.

@moimikey untuk masalah Anda, saya rasa Anda harus melewati style-loader dan memuat file css secara manual dengan dekorator. Sesuatu seperti ini mungkin:

.addDecorator(getStory => (
  <div>
    <link ... />
    {getStory()}
  </div>
))

Wow .... itu tidak pernah terlintas dalam pikiran saya ... Saya akan mencobanya.

ick tapi sekali lagi, menggunakan sass ... x_x. Saya bahkan mencoba persyaratan inline. tetapi saya tidak memiliki banyak keberuntungan. itu akan menjadi solusi yang sangat baik untuk css langsung :)

jadi @mnmtanish. terima kasih atas bimbingannya Saya telah memecahkan masalah saya dengan inspirasi Anda:

.addDecorator((getStory) => {
  require('./story.sass');
  return getStory();
})

mmm jadi satu-satunya masalah sekarang adalah ketika Anda menavigasi dari satu cerita ke cerita lain, itu menumpuk gaya :(

Mungkin pemuat gaya dapat dikonfigurasi untuk memasukkannya di tempat lain selain HEAD sehingga dapat dihapus. Saya belum melakukan ini jadi tidak yakin apakah itu mungkin. Lihat ini .

Bukankah tanggung jawab buku cerita react untuk memuat komponen dalam isolasi lengkap dan dengan demikian hanya menjalankan JavaScript / CSS yang terkait dengan cerita yang dipilih saat ini?

Apakah ini terkait dengan https://github.com/storybooks/react-storybook/issues/686?

@ConneXNL ya. poin yang bagus. ini benar ...; X

@mnmtanish tanggapan saya berikutnya tentu saja mengarah ke masalah lain, insertAt mungkin berguna, tetapi ini hanya tersedia di versi terbaru style-loader , yang tidak mungkin digunakan dengan buku cerita, karena ia menggunakan [email protected] . buku cerita masih menggunakan 0.x . versi terbaru yang mungkin bisa kita gunakan adalah [email protected] : (...

Hai @moimikey mungkin Anda dapat mencoba pendekatan yang terakhir disebutkan dengan rilis alfa?

Bukankah lebih baik / lebih aman membuat elemen iframe baru saat bertukar cerita?

Itu aman, dan juga buruk untuk kinerja.

Iframe baru harus mengurai javascript, mengurai CSS, menghubungkan ke saluran postmessage, menyambungkan kembali ke websocket, dan mungkin lebih banyak lagi.

Mungkin menghapus <style> elemen di story-switch cukup untuk menyelesaikan masalah ini?

Tentu saja tidak ada gaya global dan semuanya diatur dengan nama yang tepat, ini tidak akan menjadi masalah langsung. Saya kira angan-angan. 😃

Jika seseorang dapat membuktikan pernyataan di atas salah atau dapat menunjukkan bahwa tidak ada konsekuensi negatif, saya mendengarkan!

Saya melakukan beberapa tes hari ini. Pola dekorator bekerja dengan baik sehingga gaya hanya disuntikkan setelah Anda beralih ke cerita. Namun, saya memiliki masalah yang sama di mana gaya tidak dihapus saat bertukar cerita.

Saya bermain-main dengan menghapus gaya di dekorator tetapi tampaknya gaya yang diperlukan hanya diterapkan sekali. Apakah mungkin untuk memicu kembali Requirement ()? Saya mencoba menggunakan singleton: false tetapi itu tidak menyelesaikan masalah.

Saya hampir ragu untuk menyarankan ini, tetapi Anda dapat mencoba merusak cache webpack:
https://webpack.github.io/docs/api-in-modules.html#advanced

Ini adalah dokumen webpack 1, tetapi mungkin masih berfungsi.

Ide: Anda bisa menulis dekorator yang menghapus semua <style>...</style> saat bertukar cerita. Untuk membersihkan gaya yang tidak relevan dengan cerita saat ini.

Saya hampir sampai. Dalam konfigurasi webpack yang saya gunakan
'style-loader/useable' instead of 'style-loader',

Ini menambahkan API untuk Anda kerjakan. .use () untuk menambahkan gaya, unuse () untuk menghapusnya. Dalam file cerita saya, saya menggunakan dekorator seperti:

.addDecorator((c) => <ReactStylesheet stylesheets={[require('./stories.scss')]}>{ c() }</ReactStylesheet> )

Menggunakan Komponen React berikut untuk menambah dan menghapus gaya.

import * sebagai React from 'react';

export class ReactStylesheet extends React.Component{

    componentWillUnmount(){
        let stylesheets = Array.isArray(this.props.stylesheets) ? this.props.stylesheets : [this.props.stylesheets];
        stylesheets.forEach((stylesheet) => {
            console.log("Unmounting....");
            stylesheet.unuse();
        });

    }

    componentDidMount(){
         let stylesheets = Array.isArray(this.props.stylesheets) ? this.props.stylesheets : [this.props.stylesheets];
        stylesheets.forEach((stylesheet) => {
            console.log("Mounting....");
            stylesheet.use();
        });
    }

    render(){
        return this.props.children;
    }
}

Mengubah stylesheet dengan benar akan memuat ulang gaya. Beralih ke cerita yang berbeda unuse () dipanggil dan stylesheet dibersihkan. Namun, metode ini berhenti saat menambahkan kembali gaya karena metode ini mengiklankan versi stylesheet yang tidak diperbarui. Melakukan perubahan apa pun setelah itu juga memunculkan kesalahan ini:

Uncaught (in promise) TypeError: Cannot read property 'refs' of undefined
    at update (webpack:///./~/style-loader/addStyles.js?:63:4)
    at eval (webpack:///./src/Component/stories.scss?:32:4)
    at Object.hotApply [as apply] (http://dev.test:6006/static/preview.bundle.js:499:14)
    at cb (webpack:///(webpack)-hot-middleware/process-update.js?:52:36)
    at eval (webpack:///(webpack)-hot-middleware/process-update.js?:68:13)
    at <anonymous>

Saya tidak yakin bagaimana cara memperbarui pernyataan Requirement agar mengarah ke versi HRM terbaru.

Pekerjaan investigasi yang fantastis! Saya mencari sesuatu seperti ini, sebelumnya dan tidak dapat menemukannya.

Adakah yang bisa kami lakukan di sisi ini untuk membantu

solusinya semakin dekat. gagasan tentang stylesheet.use() dan unuse asing bagi saya, tetapi tampaknya ini sudah berada di jalur yang benar.

ini juga hal menarik lainnya untuk buku cerita sandboxing https://github.com/Wildhoney/ReactShadow

Halo semuanya! Sepertinya tidak banyak yang terjadi dalam masalah ini belakangan ini. Jika masih ada pertanyaan, komentar, atau bug, silahkan lanjutkan pembahasannya. Sayangnya, kami tidak punya waktu untuk membahas setiap masalah. Kami selalu terbuka untuk kontribusi, jadi kirimkan permintaan bantuan jika Anda ingin membantu. Masalah yang tidak aktif akan ditutup setelah 60 hari. Terima kasih!

@ConneXNL untuk menyelesaikan masalah ini, apakah menurut Anda Anda dapat membantu kami meningkatkan dokumen dalam hal ini?

Jika Anda tidak dapat menemukan tempat yang baik untuk itu, cukup tempelkan di suatu tempat dalam format penurunan harga. Aku akan mengurusnya.

🙇

Halo semuanya! Sepertinya tidak banyak yang terjadi dalam masalah ini belakangan ini. Jika masih ada pertanyaan, komentar, atau bug, silahkan lanjutkan pembahasannya. Sayangnya, kami tidak punya waktu untuk membahas setiap masalah. Kami selalu terbuka untuk kontribusi, jadi kirimkan permintaan bantuan jika Anda ingin membantu. Masalah yang tidak aktif akan ditutup setelah 60 hari. Terima kasih!

Hai, ini aku lagi! Saya akan menutup masalah ini untuk membantu pengelola kami fokus pada peta jalan pengembangan saat ini. Jika masalah yang disebutkan masih menjadi perhatian, buka tiket baru dan sebutkan yang lama. Bersulang dan terima kasih telah menggunakan Buku Cerita!

Masalah yang sama di sini, buku cerita tidak mengisolasi setiap cerita, membuatnya tidak dapat digunakan untuk pengujian visual / pengujian penerimaan.

Menempatkan akar bayangan di sekitar cerita akan langsung menyelesaikan masalah ini tanpa masalah kinerja @ndelangen

@bennypowers menarik! apakah Anda akan memiliki contoh kode tentang cara mencapai itu? 🙇

Mungkin menarik juga untuk @shilman .

Hai. Saya juga mengalami masalah ini
Apakah ini sudah diperbaiki atau ada solusi lain?

@delangen

Jalan tercepat untuk kepuasan sini mungkin @moimikey 's saran untuk digunakan ReactShadow

Strategi yang diambil mungkin untuk membungkus root dalam komponen ReactShadow, lalu membawa style dengan adoptedStyleSheets (atau elemen <style> untuk browser yang tidak mendukung)

https://github.com/storybookjs/storybook/blob/ba74d889fcfd87849a6ae9369f5e4176e8149d33/lib/core/src/client/preview/start.js#L253

Silakan buka kembali ini, masalah ini membuat penambahan gaya kustom per cerita menjadi sangat sulit. Saya memiliki cerita MDX yang benar-benar terpisah yang menerapkan gaya kustom untuk contohnya, dan secara global menyertakan semua gaya dari setiap cerita membuat kasus penggunaan ini tidak dapat dipertahankan.

edit: Terima kasih !!!

Saya berharap ini diselesaikan pada 21 Maret 2020.

@moimikey Ada minat untuk melakukan ini? Cara terbaik untuk memastikan itu dilakukan pada tanggal tertentu ... 😉

IMO kita harus menambahkan parameter atau addon untuk menangani fitur ini daripada menambahkan fungsionalitas khusus hanya untuk stylesheet. Ini akan menyebabkan perilaku yang tidak konsisten antara stylesheet dan skrip. Tapi mungkin ini saat yang tepat untuk mempertimbangkan bagaimana seharusnya "isolasi"?

Saya menulis PoC singkat untuk pendekatan addon, untuk siapa bertanya-tanya itu mungkin.
https://github.com/pocka/storybook-addon-css

@pocka Anda AWESOME! 💯

yazzzzz.dll cheers @pocka

Perlu diperhatikan bahwa solusi

Sunting: Saya buruk, itu pasti terjadi! Anda perlu memiliki style-loader 1.x + dan kemudian melakukan sesuatu seperti ini:

--- a/components/grid/GridChild.stories.mdx
+++ b/components/grid/GridChild.stories.mdx
@@ -1,7 +1,9 @@
 import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
 import { GridContainer, GridRow, GridChild } from './';
 import '../../shared/critical-path.scss';
-import 'o-grid/demos/src/scss/_demos.scss';
+import demoStylesModule from '!!style-loader?injectType=lazyStyleTag!css-loader!sass-loader!o-grid/demos/src/scss/_demos.scss?story';
+
+export const demoStyles = Promise.resolve(demoStylesModule);

 <Meta title="Core|Grid/GridChild" component={GridChild} />

@@ -37,7 +39,12 @@ You supply it a `colspan` prop in one of the following formats:
     ```

 <Preview>
-  <Story name="Default unresponsive columns">
+  <Story
+    name="Default unresponsive columns"
+    parameters={{
+      styles: [demoStyles],
+    }}
+  >
     <GridContainer>
       <GridRow>
         <GridChild colspan="1">

@tokopedia
Terima kasih telah menunjukkannya, saya benar-benar lupa tentang MDX: no_mouth:
Saya memperbarui PoC dan menambahkan contoh MDX .

Dengan pendekatan addon (per gaya cerita), berbeda dengan pendekatan cakupan file (per gaya file), tab Dokumen akan mendapatkan semua stylesheet dari setiap cerita. Dalam contoh saya , kisah "foo" dan "baz" mengimpor foo.css dan kisah "bar" mengimpor bar.css , lalu tab Dokumen mendapatkan foo.css dan bar.css . Saya pikir ini tidak dapat dihindari dan saya tidak tahu ini dapat diterima atau tidak.

@pocka Saya pikir pendekatan itu mungkin berhasil dengan baik dengan https://github.com/storybookjs/storybook/tree/next/addons/cssresources WDYT?

@delangen
Ah benar!

foo.story = {
  parameters: {
    cssresources: [
      {
        id: 'foo',
        code: `<style>${require('!to-string-loader!css-loader!./foo.css')}</style>`,
        picked: true
      }
    ]
  }
}

Satu kekhawatiran: jika pengguna mengimpor stylesheet yang sangat besar, tab "Sumber Daya CSS" akan berantakan.

@pocka benar, saya pikir addon cssresources dapat sangat ditingkatkan di departemen itu. Apakah Anda ingin menerimanya?

@delangen
Ya: smiley:

Ngomong-ngomong, bagaimana menurut Anda tentang mengizinkan pengguna menulis raw-webpack-query? (seperti !to-string-loader!... ) Saya rasa kita membutuhkan banyak ilmu hitam dalam kode addon jika kita ingin menyingkirkan ini ...

Saya pikir kami mendukung babel-macro secara default, sehingga pengguna dapat menggunakan macro-preval untuk memasukkan file-konten ke dalam bundel juga?

Saya tidak tahu itu, saya akan segera melihatnya!

Halo, saya mengalami masalah yang sama.

Untuk siapa pun yang menghadapi masalah ini, silakan coba solusi ini . (Ini membutuhkan sedikit pengetahuan webpack yang dalam).


@ndelangen Saya melihat makro tetapi yang diaktifkannya adalah "memuat file CSS dari sistem file", bukan? Saya pikir banyak pengguna ingin mengimpor file SASS, Less, Stylus, CSS dengan PostCSS, atau lain-lain, sehingga pendekatan itu tidak akan memenuhi kebutuhan. Ide saya saat ini adalah addon CSSResource menambahkan aturan yang mengimpor file CSS dengan to-string-loader (atau file-loader ) sehingga pengguna dapat mengimpor file CSS kemudian menggunakannya untuk addon.

// adding a rule like this
{
  test: /\.css$/,
  resourceQuery: /cssresources/,
  use: ['to-string-loader', 'css-loader', 'postcss-loader']
}

Untuk praprosesor, opsi untuk menyesuaikan test dan use juga diperlukan. Dimungkinkan untuk memilih aturan untuk file gaya kemudian memodifikasinya dengan oneOf tetapi itu akan sangat rumit ...

Bagaimana menurut anda?

@pocka ya kedengarannya seperti konsep yang menarik!

Hai, bertanya-tanya apakah ini masih dikerjakan? Saya juga mengalami masalah tersebut, saya mengetahui pekerjaan sekitar tetapi ingin tahu apakah perbaikan akan tersedia dalam waktu dekat.

Hai, dapatkah Anda memberikan contoh solusi untuk Vue Story.?

Sejauh yang saya tahu, solusinya masih menghasilkan stylesheet bertumpuk setelah tampilan awal, setidaknya jika Anda menggunakan add-on Dokumen. 😕

Tanpa maksud tidak hormat, saya menemukan pendekatan addon @pocka agak kurang, karena tidak mengisolasi gaya yang telah diimpor dalam file komponen dibandingkan dengan di file cerita, yang menurut saya merupakan pola yang lebih umum. Keinginan pribadi saya — Saya curiga ini mungkin dibagikan oleh orang lain di utas ini — adalah dapat memiliki import './Button.css' di dalam Button.jsx yang hanya digunakan dalam file cerita di mana Button.jsx diimpor. Penataan gaya per cerita, dengan cara yang disediakan @pocka , bagi saya hampir tidak sepenting memastikan bahwa tidak ada komponen yang tidak mengimpor Button (secara langsung atau tidak langsung) tidak terpengaruh oleh aturan CSS apa pun dari Button.css . (Kekhawatiran di sini adalah ingin memastikan bahwa OtherWidget.css , katakanlah, tidak kekurangan beberapa aturan yang secara tidak sengaja berakhir dengan Button.css — mungkin mereka terlewatkan dalam refactoring atau semacamnya — dan hilang itu karena cerita untuk OtherWidget mendapatkan semua CSS yang diimpor secara statis dari seluruh aplikasi, jadi OtherWidget masih terlihat bagus di Storybook.)

Yang saya pikir akan saya lakukan adalah mengubah semua pemuat CSS untuk disuntikkan dengan lazyStyleTag , dan kemudian menggunakan API webpack untuk menghasilkan modul baru yang mengelompokkan modul CSS dengan file cerita yang pada akhirnya membutuhkannya dan mendengarkan perubahan cerita acara untuk mengaktifkan dan menonaktifkan modul yang sesuai.
Apakah pendekatan ini telah dipertimbangkan dan dibuang, atau adakah masalah yang Anda lihat sekarang? Saya rasa saya bisa melakukan semuanya dalam addon Storybook, tetapi mungkin lebih bersih jika diintegrasikan ke dalam Storybook sebagai fitur inti.

Jika Anda menginginkan enkapsulasi gaya yang kuat, browser disertakan dengannya. Dengan risiko mengungkap ketidaktahuan saya sendiri di sini, saya masih tidak yakin mengapa semua JavaScript userland ini (dengan biaya kompleksitas yang terkait) diperlukan untuk mencapai sesuatu yang sudah ada di dalamnya dan siap digunakan.

Mengapa tidak mengubah DOM setiap cerita menjadi akar bayangan dengan sesuatu seperti ini

customElements.define('encapsulated-story', class EncapsulatedStory extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
  }

  /* not sure why we'd need this getter, but let's say */
  get storyHTML() {
    return this.shadowRoot.innerHTML;
  }

  set storyHTML(string) {
    this.shadowRoot.innerHTML = storyHTML;
  }
});

dan kapan pun ceritanya berubah

encapsulatedStory.storyHTML = theStoryDOMStringWithAllStyleTags;

dan selesai? Di sini, theStoryDOMStringWithAllStyleTags hanyalah rangkaian HTML cerita dengan semua gaya terkait yang digabungkan sebagai tag <style> sebaris. Story dapat mengatur gaya elemen host dengan pemilih :host , seperti biasa.

Itu adalah titik awal telanjang-minumum, yang bisa dibangun nanti mungkin dengan beberapa kode pustaka, tapi setidaknya itu akan mencapai tujuan enkapsulasi gaya yang kuat tanpa perlu semua API baru yang diusulkan ini.

Bagaimana cara kerjanya dengan webpack? Webpack mengemas semuanya menjadi bundel JavaScript, bukan string DOM; dan cara webpack dikonfigurasi saat ini, ia mengemas semua cerita menjadi satu bundel. Saya rasa shadow root tidak membantu ketika JavaScript yang sedang dimuat (hot-re) memasukkan gaya langsung ke kepala dokumen. Anda perlu melakukan beberapa konfigurasi webpack berbulu dengan satu atau lain cara untuk mengubahnya.

Menggunakan shadow DOM untuk mengisolasi setiap cerita sepenuhnya juga berarti mereplikasi tag gaya yang dibagikan oleh banyak cerita menjadi setiap cerita; menggunakan gaya bersama yang dibundel oleh webpack akan lebih efisien. Mungkin tidak cukup untuk membuat perbedaan besar, tetapi mungkin cukup untuk mengimbangi manfaat, jika ada, menggunakan shadow DOM daripada menggunakan lazyStyleTag (yang menurut saya adalah satu-satunya bagian kompleksitas yang akan dilakukan oleh shadow root. menyelamatkanmu).

Saya juga tertarik melihat ini diperbaiki cepat atau lambat.

Itu aman, dan juga buruk untuk kinerja.

Iframe baru harus mengurai javascript, mengurai CSS, menghubungkan ke saluran postmessage, menyambungkan kembali ke websocket, dan mungkin lebih banyak lagi.

@ndelangen menyesal mengutip Anda dari tahun 2017, tetapi apakah ini masih perspektif Anda, bahwa memuat ulang iframe terlalu mahal? Browser melakukan hal semacam ini secara konstan; mereka mungkin sangat dioptimalkan untuk itu. Dalam hal ini akan lebih cepat daripada pemuatan halaman biasa, karena tidak ada permintaan jaringan yang terlibat.

Bagi saya, manfaatnya lebih besar daripada biayanya, karena saya sangat menyukai iframe baru untuk setiap cerita. Saya akan mentolerir penundaan hingga 600ms untuk kemewahan seperti itu.

(Kasus penggunaan saya adalah bahwa saya mencoba untuk membuat beberapa komponen angularjs warisan di buku cerita, dan katakan saja bahwa komponen tidak terlalu murni. Mereka sangat stateful; mereka memiliki efek samping dan menggunakan layanan angularjs yang juga sangat tegas. Hal-hal pecah dengan cara yang tidak terduga.)

Satu ide untuk permukaan API adalah mengonfigurasi buku cerita di .storybook/manager.js .

addons.setConfig({
  refreshBetweenStories: true,
})

Ini bisa dianggap sebagai pengaturan UI jika Anda memiringkan kepala dengan benar.

Tidak akan ada biaya waktu proses untuk orang yang tidak mengaktifkan tanda ini, dan bagi mereka yang mengaktifkannya, mereka _ benar-benar_ membutuhkannya, jadi penundaan seperti itu dapat ditoleransi.

Jika Anda ingin masalah ini diperbaiki, beri suara positif dengan menambahkan 👍 ke deskripsi masalah. Kami menggunakan ini untuk membantu memprioritaskan!

.addDecorator ((getStory) => {
membutuhkan ('./ story.sass');
return getStory ();
})

HAI!!!!
dimana saya bisa meletakkan yang ini? !!!

Apakah halaman ini membantu?
0 / 5 - 0 peringkat